home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume39 / sybperl / part01 < prev    next >
Encoding:
Text File  |  1993-09-25  |  61.0 KB  |  2,184 lines

  1. Newsgroups: comp.sources.misc
  2. From: mpeppler@itf.ch (Michael Peppler)
  3. Subject: v39i101:  sybperl - Sybase DB-library extensions to Perl, v1.8, Part01/03
  4. Message-ID: <csm-v39i101=sybperl.132552@sparky.Sterling.COM>
  5. X-Md4-Signature: 40b3185c7c0a4ebda79de2f788a1222e
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Sat, 25 Sep 1993 18:26:25 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: mpeppler@itf.ch (Michael Peppler)
  12. Posting-number: Volume 39, Issue 101
  13. Archive-name: sybperl/part01
  14. Environment: UNIX, Perl, Sybase
  15. Supersedes: sybperl: Volume 37, Issue 33-34
  16.  
  17. This is Sybperl release 1.8.
  18.  
  19. Sybperl is an extension to Perl which allows you to access Sybase
  20. databases directly from Perl scripts using standard OpenClient (aka
  21. DB-Library) calls.
  22.  
  23. >From the CHANGES file:
  24.     1.008   Added user settable variables to control whether
  25.         Sybperl returns 'NULL' or Perl's 'undef' value on NULL
  26.         values from a query, whether numeric results are kept
  27.         in native format, and whether binary data should be
  28.         preceded by '0x' (suggested by Steve Baumgartner).
  29.         Actually made $DBstatus visible (it was documented but
  30.         not usable up to now...).
  31.         Passing an undef'd variable to &bcp_sendrow will cause
  32.         a NULL value to be sent to the server for that column.
  33.     1.007   Added &dbmny* calls and code to circumvent weird
  34.         package/usub interaction bug, both contributed by Jeff
  35.         Wong.
  36.         Added &bcp_* calls.
  37.         Added &dbretdata() call (returns an array, possibly
  38.         associative, with the return parameters of a stored
  39.         proc).
  40.         Calls to any of the routines with an undefined
  41.         DBPROCESS will now elicit a warning; previously, such
  42.         calls defaulted to using the first (default)
  43.         DBPROCESS.
  44.         Data returned from queries is not converted to char
  45.         unless its necessary - this applies mainly to types
  46.         SYBFLOAT and SYBREAL which could loose some precision
  47.         on being converted to a string via sprintf().
  48.  
  49. Michael Peppler                           mpeppler@itf.ch
  50. ITF Management SA                      mpeppler@bix.com     
  51. 13 Rue de la Fontaine                  Phone: (+4122) 312 1311
  52. CH-1204 Geneva, Switzerland            Fax:   (+4122) 312 1325
  53. ---
  54. #! /bin/sh
  55. # This is a shell archive.  Remove anything before this line, then feed it
  56. # into a shell via "sh file" or similar.  To overwrite existing files,
  57. # type "sh file -c".
  58. # Contents:  README Makefile eg lib patchlevel.h sybperl.c.A t
  59. # Wrapped by kent@sparky on Sat Sep 25 13:15:59 1993
  60. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  61. echo If this archive is complete, you will see the following message:
  62. echo '          "shar: End of archive 1 (of 3)."'
  63. if test -f 'README' -a "${1}" != "-c" ; then 
  64.   echo shar: Will not clobber existing file \"'README'\"
  65. else
  66.   echo shar: Extracting \"'README'\" \(3918 characters\)
  67.   sed "s/^X//" >'README' <<'END_OF_FILE'
  68. X    @(#)README    1.4    9/23/93
  69. X    
  70. X                 Sybperl, version 1.0
  71. X
  72. X
  73. X
  74. X   Sybperl is a set of user-defined subroutines letting you access a
  75. X   Sybase data server using Perl.
  76. X
  77. X   Requirements: Perl ver 3.0.27 or higher (4.036 strongly suggested!).
  78. X         Sybase DB-Library (aka Open Client)
  79. X
  80. X
  81. X   Compiling & Installing Sybperl:
  82. X   
  83. X   Unshar somewhere convenient, and edit Makefile to reflect your
  84. X   system setup. The following macros/defines may need to be set:
  85. X
  86. X       PERL_VERSION     Uncomment if you're using a Perl version
  87. X             earlier than 4.03
  88. X       UPERL         See the comments in the Makefile, and the
  89. X             BUGS file. The defaults should work.
  90. X       HAS_CALLBACK     This enables the use of Perl subroutines as
  91. X             DB-Library error & message handlers. This is
  92. X             a new feature of Perl 4.018, but it might
  93. X             work with earlier versions.
  94. X       DBLIBVS          The version of your OpenClient library. Valid
  95. X             values are 'undefined', DBLIB42 and DBLIB461.
  96. X             Depending on its value certain newer features
  97. X             of DBlibrary will be included in Sybperl.
  98. X       SET_VAL         If this macro is set, then attempts to set a
  99. X             Sybperl user-variable (such as
  100. X             $NO_MORE_RESULTS) will result in a fatal
  101. X             error. Otherwise such attempts are silently
  102. X             ignored.
  103. X       OLD_SYBPERL     This is a backwards compatibility flag -
  104. X             mainly for myself :-). It's main impact is to
  105. X             silently call dblogin()/dbopen() with default
  106. X             arguments if you omit to do so in the script.
  107. X       PACKAGE_BUG     There appears to be a weird bug when one
  108. X             calls usersubs from within multiple Perl
  109. X             packages. If you run into this problem, you
  110. X             can enable this macro (see also the BUGS
  111. X             file).
  112. X
  113. X   When building Sybperl under Solaris 2.x, you need to add -lnsl
  114. X   and -lsocket to the SYBLIBS variable in the Makefile.
  115. X   
  116. X   The Makefile will not attempt to build uperl.o if it can't find it.
  117. X
  118. X   You may also need to edit the lib/sybperl.pl file to addapt it to
  119. X   your environment.
  120. X
  121. X   There are some test scripts in the t directory which you can run to
  122. X   see if all is well, and to get an idea of what can be done with
  123. X   sybperl. There are also some example scripts in the 'eg' directory.
  124. X
  125. X   Sybperl has been tested succesfully in the following environments:
  126. X
  127. X   Sun Sparc, SunOS 4.1.3, Sybase 4.8, Perl 4.036
  128. X   Sun 3/80, SunOS 4.0.3, Sybase 4.0.1, Perl 4.010
  129. X   Sun Sparc, SunOS 4.1, Sybase 4.2, Perl 4.010
  130. X   Pyramid MIServer 2/2, OSx V5.1a, Sybase 4.0, Perl 4.010
  131. X   
  132. X   I use sybperl daily in a production environment on a Sun network
  133. X   (Sun 4/65s and Axil HWS 310s) under SunOS 4.1.3, with Sybase
  134. X   version 4.8 and Perl 4.036
  135. X
  136. X   FTP site: Sybperl and other DBMS related extensions to Perl can be
  137. X   found on ftp.demon.co.uk:/pub/perl/db.
  138. X   
  139. X
  140. X   BUGS:
  141. X
  142. X   Both DBlibrary and Perl define a symbol named 'savestr', resulting
  143. X   in the Perl version being called from DBlibrary. See the BUGS file
  144. X   for ways to get around this problem.
  145. X
  146. X   Memory usage can also be a problem in certain cases. Again see the
  147. X   BUGS file for details.
  148. X
  149. X
  150. X
  151. X
  152. X   
  153. X   Have fun using it and let me know of any improvements, problems,
  154. X   whatever...
  155. X
  156. X   Michael Peppler            mpeppler@itf.ch    mpeppler@bix.com
  157. X   ITF Management SA            BIX:   mpeppler                         
  158. X   13 Rue de la Fontaine        Phone: (+4122) 312 1311  
  159. X   CH-1204 Geneva, Switzerland  Fax:   (+4122) 312 1322  
  160. X
  161. X   
  162. X
  163. X                   NOTICE - Warranty and Copyright
  164. X
  165. X           
  166. X   Sybperl is not a product of ITF Management. There is no warranty,
  167. X   and no official support.
  168. X
  169. X   Sybperl is copyright, but may be freely distributed under the
  170. X   same terms as Perl itself.
  171. X
  172. X
  173. X
  174. X   My thanks to the following people for testing Perl, and suggesting
  175. X   improvements:
  176. X
  177. X   Teemu Torma            Brent Milnor
  178. X   Matthew Merzbacher        Eric Fifer
  179. X   Dan Banay            Mark Lawrence
  180. X   Jeffrey Wong            Wolfgang Richter
  181. X   Anders Ardo            Gijs Mos
  182. X   Minh Ton Ha            G. Roderick Singleton
  183. X   Peter Gutmann
  184. X
  185. END_OF_FILE
  186.   if test 3918 -ne `wc -c <'README'`; then
  187.     echo shar: \"'README'\" unpacked with wrong size!
  188.   fi
  189.   # end of 'README'
  190. fi
  191. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  192.   echo shar: Will not clobber existing file \"'Makefile'\"
  193. else
  194.   echo shar: Extracting \"'Makefile'\" \(3285 characters\)
  195.   sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  196. X#    @(#)Makefile    1.16    9/6/93
  197. X#
  198. X    
  199. XCC = gcc
  200. XPERLSRC = ..                # where to find uperl.o
  201. XSYBINCS = /usr/local/sybase/include    # where to find the sybase .h files
  202. XLOCINCS =                # other includes ?
  203. XSYBLIBDIR = /usr/local/lib        # Sybase libraries
  204. XSYBLIBS = -lsybdb            # db-library
  205. X
  206. X# Uncomment this if you are compiling sybperl for Perl version 3.xx
  207. X
  208. X# PERL_VERSION = -DVERSION3
  209. X
  210. X# The Perl/Sybase savestr() conflict.
  211. X# Both Perl and Sybase DB-Library have a function called savestr(),
  212. X# and this creates a problem when using functions such as dbcmd().
  213. X# There are several ways around this.
  214. X# You can:
  215. X#
  216. X#    - Recompile uperl.o with a -Dsavestr=psvestr (or something similar).
  217. X#    - Edit an existing uperl.o and change _savestr to _psvestr.
  218. X#
  219. X#
  220. X# To use the first option, you have to reconfigure & recompile Perl
  221. X# manually, and then set compile sybperl with the following line
  222. X# uncommented:
  223. X# UPERL = $(PERLSRC)/uperl.o
  224. X#
  225. X# The default is to use the third solution:
  226. XUPERL = uperl2.o
  227. X
  228. X
  229. XHAS_CALLBACK= -DHAS_CALLBACK        # Remove this if you don't
  230. X                    # have Perl 4 patchlevel 18
  231. X                    # User defined, perl based
  232. X                    # error/message handlers are
  233. X                    # not possible without this, however.
  234. XOLD_SYBPERL= -DOLD_SYBPERL        # some backward compatibility stuff.
  235. X
  236. XDBLIBVS = -DDBLIB461            # Comment this if your version
  237. X                    # of DBlib is older than
  238. X                    # version 4.2
  239. X
  240. X#SET_VAL = -DUSERVAL_SET_FATAL        # Uncomment this if you wish
  241. X                    # to get a fatal error message
  242. X                    # if you attempt to set on of
  243. X                    # Sybperl's variables from a
  244. X                    # script. Normally such
  245. X                    # actions are silently ignored.
  246. X
  247. X#PACKAGE_BUG = -DPACKAGE_BUG        # Uncomment to enable code
  248. X                    # to circumvent a bug that
  249. X                    # shows up when calling usubs from
  250. X                    # within nested packages.
  251. X                    
  252. X
  253. XCFLAGS = -O2 -g
  254. XCPPFLAGS = -I$(PERLSRC) -I$(LOCINCS) -I$(SYBINCS) $(PERL_VERSION) \
  255. X        $(HAS_CALLBACK) $(OLD_SYBPERL) $(DBLIBVS) \
  256. X        $(SET_VAL) $(PACKAGE_BUG)
  257. X
  258. XBINDIR = /usr/local/bin            # where does the executable go
  259. XPERLLIB = /usr/local/lib/perl        # where does lib/sybperl.pl
  260. X                    # and lib/sybdb.ph go
  261. XMANDIR = /usr/local/man            # where do we put the manual page
  262. XMANEXT = l
  263. X
  264. X
  265. Xsybperl: $(UPERL) sybperl.o
  266. X    $(CC) $(CFLAGS) -L$(SYBLIBDIR) $(UPERL) sybperl.o $(SYBLIBS) -lm -o sybperl
  267. X
  268. Xsybperl.o: sybperl.c
  269. X    $(CC) -c $(CFLAGS) $(CPPFLAGS) sybperl.c
  270. X
  271. X# Create uperl.o IF you wish to use the 3rd way of resolving the
  272. X# Perl/Sybase savestr conflict.
  273. X$(UPERL): $(PERLSRC)/uperl.o
  274. X    cp $(PERLSRC)/uperl.o $(UPERL)
  275. X    perl -p -i.bak -e 's/savestr/psvestr/g;' $(UPERL)
  276. X    rm -f $(UPERL).bak
  277. X
  278. X
  279. Xclean:
  280. X    rm -f sybperl *.o *~ core
  281. X
  282. Xinstall: sybperl
  283. X    install -s -m 775 sybperl $(BINDIR)
  284. X    cp lib/syb*.p? $(PERLLIB)
  285. X    cp sybperl.1 $(MANDIR)/man$(MANEXT)/sybperl.$(MANEXT)
  286. X
  287. Xshar:
  288. X    rm -f sybperl.shar
  289. X    shar.pl README PACKING.LST BUGS CHANGES Makefile sybperl.c \
  290. X    sybperl.1 patchlevel.h lib/sybperl.pl lib/sybdb.ph \
  291. X    lib/sybdb_redefs.pl t/sbex.pl \
  292. X    eg/sql.pl eg/space.pl eg/capture.pl eg/report.pl \
  293. X    eg/dbschema.pl eg/dbtext.pl eg/test_dbmoney.pl eg/README >sybperl.shar
  294. X
  295. X
  296. Xtar:
  297. X    rm -f sybperl.tar
  298. X    tar cvfB sybperl.tar README PACKING.LST BUGS CHANGES Makefile sybperl.c \
  299. X    sybperl.1 patchlevel.h lib/sybperl.pl lib/sybdb.ph \
  300. X    lib/sybdb_redefs.pl t/sbex.pl \
  301. X    eg/sql.pl eg/space.pl eg/capture.pl eg/report.pl \
  302. X    eg/dbschema.pl eg/dbtext.pl eg/test_dbmoney.pl eg/README
  303. X
  304. X
  305. X
  306. X
  307. X
  308. END_OF_FILE
  309.   if test 3285 -ne `wc -c <'Makefile'`; then
  310.     echo shar: \"'Makefile'\" unpacked with wrong size!
  311.   fi
  312.   # end of 'Makefile'
  313. fi
  314. if test ! -d 'eg' ; then
  315.     echo shar: Creating directory \"'eg'\"
  316.     mkdir 'eg'
  317. fi
  318. if test ! -d 'lib' ; then
  319.     echo shar: Creating directory \"'lib'\"
  320.     mkdir 'lib'
  321. fi
  322. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  323.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  324. else
  325.   echo shar: Extracting \"'patchlevel.h'\" \(79 characters\)
  326.   sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  327. X
  328. X/*     @(#)patchlevel.h    1.2    9/6/93     */
  329. X
  330. X#define VERSION 1
  331. X#define PATCHLEVEL 8
  332. X
  333. X
  334. END_OF_FILE
  335.   if test 79 -ne `wc -c <'patchlevel.h'`; then
  336.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  337.   fi
  338.   # end of 'patchlevel.h'
  339. fi
  340. if test -f 'sybperl.c.A' -a "${1}" != "-c" ; then 
  341.   echo shar: Will not clobber existing file \"'sybperl.c.A'\"
  342. else
  343.   echo shar: Extracting \"'sybperl.c.A'\" \(48039 characters\)
  344.   sed "s/^X//" >'sybperl.c.A' <<'END_OF_FILE'
  345. Xstatic char SccsId[] = "@(#)sybperl.c    1.25    9/13/93";
  346. X/************************************************************************/
  347. X/*    Copyright 1991, 1992, 1993 by Michael Peppler            */
  348. X/*                               and ITF Management SA             */
  349. X/*      Portions Copyright (c) 1993 Commercial Dynamics Pty Ltd         */
  350. X/*                                    */
  351. X/*    Full ownership of this software, and all rights pertaining to     */
  352. X/*    the for-profit distribution of this software, are retained by     */
  353. X/*    Michael Peppler and ITF Management SA.  You are permitted to     */
  354. X/*    use this software without fee.  This software is provided "as     */
  355. X/*    is" without express or implied warranty.  You may redistribute     */
  356. X/*    this software, provided that this copyright notice is retained,    */
  357. X/*    and that the software is not distributed for profit.  If you     */
  358. X/*    wish to use this software in a profit-making venture, you must     */
  359. X/*    first license this code and its underlying technology from     */
  360. X/*    ITF Management SA.                         */
  361. X/*                                    */
  362. X/*    Bottom line: you can have this software, you can use it, you     */
  363. X/*    can give it away.  You just can't sell any or all parts of it     */
  364. X/*    without prior permission from ITF Management SA.        */
  365. X/************************************************************************/
  366. X
  367. X/* sybperl.c
  368. X *
  369. X * Call Sybase DB-Library functions from Perl.
  370. X * Written by Michael Peppler (mpeppler@itf.ch)
  371. X * ITF Management SA, 13 rue de la Fontaine
  372. X * CH-1204 Geneva, Switzerland
  373. X * Tel: (+4122) 312 1311 Fax: (+4122) 312 1322
  374. X */
  375. X
  376. X#include "EXTERN.h"
  377. X#include "perl.h"
  378. X#undef MAX
  379. X#undef MIN
  380. X
  381. X#if defined(VERSION3)
  382. X#define str_2mortal(s)        str_2static(s)
  383. X#endif
  384. X
  385. X#include <sybfront.h>
  386. X#include <sybdb.h>
  387. X#include <syberror.h>
  388. X
  389. X#include "patchlevel.h"
  390. X
  391. Xextern int wantarray;
  392. X
  393. X#if defined(DBLIB461)
  394. Xstatic void new_mny4tochar();  /* forward declaration */
  395. Xstatic void new_mnytochar();   /* forward declaration */
  396. X#endif
  397. X
  398. X/* 
  399. X * The variables that the Sybase routines set, and that you may want 
  400. X * to test in your Perl script. These variables are READ-ONLY.
  401. X */
  402. Xenum uservars
  403. X{
  404. X    UV_SUCCEED,            /* Returns SUCCEED */
  405. X    UV_FAIL,            /* Returns FAIL */
  406. X    UV_NO_MORE_ROWS,        /* Returns NO_MORE_ROWS */
  407. X    UV_NO_MORE_RESULTS,        /* Returns NO_MORE_RESULTS */
  408. X    UV_ComputeId,        /* Returns the compute id of the row (in dbnextrow()) */
  409. X    UV_SybperlVer,        /* Returns Sybperl Version/Patchlevel */
  410. X    UV_DBstatus,        /* The status value of the last dbnextrow() call */
  411. X    /* The following enum definitions are also for Sybase OpenClient R4.6.1
  412. X     * read-only perl variable synthesis.  See above for format ...
  413. X     */
  414. X#if defined(DBLIB461)
  415. X    UV_STDEXIT,
  416. X    UV_ERREXIT,
  417. X    UV_INT_EXIT,
  418. X    UV_INT_CONTINUE,
  419. X    UV_INT_CANCEL,
  420. X    UV_INT_TIMEOUT,
  421. X    UV_MORE_ROWS,
  422. X    UV_REG_ROW,
  423. X    UV_BUF_FULL,
  424. X    UV_NO_MORE_PARAMS,
  425. X    UV_DBSAVE,
  426. X    UV_DBNOSAVE,
  427. X    UV_DBNOERR,
  428. X    UV_DB_PASSTHRU_MORE,
  429. X    UV_DB_PASSTHRU_EOM,
  430. X    UV_DBNOPROC,
  431. X    UV_EXCEPTION,
  432. X    UV_EXSIGNAL,
  433. X    UV_EXSCREENIO,
  434. X    UV_EXDBLIB,
  435. X    UV_EXFORMS,
  436. X    UV_EXCLIPBOARD,
  437. X    UV_EXLOOKUP,
  438. X    UV_EXINFO,
  439. X    UV_EXUSER,
  440. X    UV_EXNONFATAL,
  441. X    UV_EXCONVERSION,
  442. X    UV_EXSERVER,
  443. X    UV_EXTIME,
  444. X    UV_EXPROGRAM,
  445. X    UV_EXRESOURCE,
  446. X    UV_EXCOMM,
  447. X    UV_EXFATAL,
  448. X    UV_EXCONSISTENCY,
  449. X#endif
  450. X    UV_DB_IN,
  451. X    UV_DB_OUT,
  452. X    UV_BCPMAXERRS,
  453. X    UV_BCPFIRST,
  454. X    UV_BCPLAST,
  455. X    UV_BCPBATCH,
  456. X    UV_DBTRUE,
  457. X    UV_DBFALSE,
  458. X#if defined(PACKAGE_BUG)
  459. X    UV_PACKAGE_BUG,
  460. X#endif
  461. X    UV_dbNullIsUndef,
  462. X    UV_dbKeepNumeric,
  463. X    UV_dbBin0x,
  464. X};
  465. X
  466. X/* 
  467. X * User subroutines that we have implemented. I've found that I can do 
  468. X * all the stuff I want to with this subset of DB-Library. Let me know 
  469. X * if you implement further routines.
  470. X * The names are self-explanatory.
  471. X */
  472. Xenum usersubs
  473. X{
  474. X    US_dblogin,            /* This also performs the first dbopen()  */
  475. X    US_dbopen,
  476. X    US_dbclose,
  477. X    US_dbcmd,
  478. X    US_dbsqlexec,
  479. X    US_dbresults,
  480. X    US_dbnextrow,
  481. X    US_dbcancel,
  482. X    US_dbcanquery,
  483. X    US_dbexit,
  484. X    US_dbuse,
  485. X#ifdef HAS_CALLBACK
  486. X    US_dberrhandle,
  487. X    US_dbmsghandle,
  488. X#endif
  489. X    US_dbstrcpy,
  490. X    US_DBMORECMDS,
  491. X    US_DBCMDROW,
  492. X    US_DBROWS,
  493. X    US_DBCOUNT,
  494. X    US_DBCURCMD,
  495. X    US_dbhasretstat,
  496. X    US_dbretstatus,
  497. X    US_dbretdata,
  498. X    US_dbwritetext,
  499. X    US_dbcoltype,
  500. X    US_dbcolname,
  501. X    US_dbcollen,
  502. X    US_dbnumcols,
  503. X#if defined(DBLIB461)
  504. X    US_dbsafestr,
  505. X    US_dbmny4add,
  506. X    US_dbmny4cmp,
  507. X    US_dbmny4divide,
  508. X    US_dbmny4minus,
  509. X    US_dbmny4mul,
  510. X    US_dbmny4sub,
  511. X    US_dbmny4zero,
  512. X    US_dbmnyadd,
  513. X    US_dbmnycmp,
  514. X    US_dbmnydivide,
  515. X    US_dbmnyminus,
  516. X    US_dbmnymul,
  517. X    US_dbmnysub,
  518. X    US_dbmnyzero,
  519. X    US_dbmnydec,
  520. X    US_dbmnydown,
  521. X    US_dbmnyinc,
  522. X    US_dbmnyinit,
  523. X    US_dbmnymaxneg,
  524. X    US_dbmnymaxpos,
  525. X    US_dbmnyndigit,
  526. X    US_dbmnyscale,
  527. X    US_dbrecftos,
  528. X#endif
  529. X    US_BCP_SETL,
  530. X    US_bcp_getl,
  531. X    US_bcp_init,
  532. X    US_bcp_meminit,
  533. X    US_bcp_sendrow,
  534. X    US_bcp_batch,
  535. X    US_bcp_done,
  536. X    US_bcp_control,
  537. X    US_bcp_columns,
  538. X    US_bcp_colfmt,
  539. X    US_bcp_collen,
  540. X    US_bcp_exec,
  541. X    US_bcp_readfmt,
  542. X    US_bcp_writefmt,
  543. X};
  544. X
  545. X#ifndef MAX_DBPROCS
  546. X#define MAX_DBPROCS 25        /* Change this if you really want your perl script to talk to */
  547. X                /* more than 25 dataserver connections at a time ...*/
  548. X#endif
  549. X
  550. X
  551. X /* some info that needs to be maintained on a per DBPROCESS basis. */
  552. Xstruct dbProcInfo
  553. X{
  554. X    DBPROCESS *dbproc;
  555. X    BYTE **colPtr;
  556. X};
  557. X
  558. Xstatic LOGINREC *login;
  559. Xstatic struct dbProcInfo dbProc[MAX_DBPROCS];
  560. Xstatic int exitCalled = 0;    /* Set to 1 if dbexit() has been called. */
  561. Xstatic int ComputeId;
  562. Xstatic int DBstatus;        /* Set by dbnextrow() */
  563. Xstatic int dbNullIsUndef;
  564. Xstatic int dbKeepNumeric;
  565. Xstatic int dbBin0x;
  566. X
  567. X/* Stack pointer for the error routines.  This is set to the stack pointer
  568. X   when entering into the sybase subroutines.  Error and message
  569. X   handling needs this.  */
  570. X
  571. Xstatic int perl_sp;
  572. X
  573. X/* Current error handler name. */
  574. X
  575. Xstatic char *err_handler_sub;
  576. X
  577. X/* Current message handler subroutine name */
  578. X
  579. Xstatic char *msg_handler_sub;
  580. X
  581. X/* Macro to access the stack.  This is necessary since error handlers may
  582. X   call perl routines and thus the stack may change.  I hope most compilers
  583. X   will optimize this reasonably. */
  584. X
  585. X#define STACK(SP) (stack->ary_array + (SP))
  586. X
  587. X
  588. Xstatic int usersub();
  589. Xstatic int userset();
  590. Xstatic int userval();
  591. Xstatic int err_handler(), msg_handler();
  592. Xstatic int getDbProc();
  593. X
  594. Xint
  595. Xuserinit()
  596. X{
  597. X    init_sybase();
  598. X}
  599. X
  600. Xint
  601. Xinit_sybase()
  602. X{
  603. X    struct ufuncs uf;
  604. X    char *filename = "sybase.c";
  605. X
  606. X    if (dbinit() == FAIL)    /* initialize dblibrary */
  607. X    exit(ERREXIT);
  608. X/*
  609. X * Install the user-supplied error-handling and message-handling routines.
  610. X * They are defined at the bottom of this source file.
  611. X */
  612. X    dberrhandle(err_handler);
  613. X    dbmsghandle(msg_handler);
  614. X
  615. X    if(MAX_DBPROCS > 25)
  616. X    dbsetmaxprocs(MAX_DBPROCS);
  617. X    
  618. X    uf.uf_set = userset;
  619. X    uf.uf_val = userval;
  620. X
  621. X#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)
  622. X
  623. X    MAGICVAR("SUCCEED",    UV_SUCCEED);
  624. X    MAGICVAR("FAIL",UV_FAIL);
  625. X    MAGICVAR("NO_MORE_ROWS",    UV_NO_MORE_ROWS);
  626. X    MAGICVAR("NO_MORE_RESULTS",    UV_NO_MORE_RESULTS);
  627. X    MAGICVAR("ComputeId",    UV_ComputeId);
  628. X    MAGICVAR("SybperlVer",    UV_SybperlVer);
  629. X    MAGICVAR("DBstatus",    UV_DBstatus);
  630. X#if defined(DBLIB461)
  631. X    MAGICVAR("STDEXIT",          UV_STDEXIT);
  632. X    MAGICVAR("ERREXIT",          UV_ERREXIT);
  633. X    MAGICVAR("INT_EXIT",         UV_INT_EXIT);
  634. X    MAGICVAR("INT_CONTINUE",     UV_INT_CONTINUE);
  635. X    MAGICVAR("INT_CANCEL",       UV_INT_CANCEL);
  636. X    MAGICVAR("INT_TIMEOUT",      UV_INT_TIMEOUT);
  637. X    MAGICVAR("MORE_ROWS",        UV_MORE_ROWS);
  638. X    MAGICVAR("REG_ROW",          UV_REG_ROW);
  639. X    MAGICVAR("BUF_FULL",         UV_BUF_FULL);
  640. X    MAGICVAR("NO_MORE_PARAMS",   UV_NO_MORE_PARAMS);
  641. X    MAGICVAR("DBSAVE",           UV_DBSAVE);
  642. X    MAGICVAR("DBNOSAVE",         UV_DBNOSAVE);
  643. X    MAGICVAR("DBNOERR",          UV_DBNOERR);
  644. X    MAGICVAR("DB_PASSTHRU_MORE", UV_DB_PASSTHRU_MORE);
  645. X    MAGICVAR("DB_PASSTHRU_EOM",  UV_DB_PASSTHRU_EOM);
  646. X    MAGICVAR("DBNOPROC",         UV_DBNOPROC);
  647. X    MAGICVAR("EXCEPTION",        UV_EXCEPTION);
  648. X    MAGICVAR("EXSIGNAL",         UV_EXSIGNAL);
  649. X    MAGICVAR("EXSCREENIO",       UV_EXSCREENIO);
  650. X    MAGICVAR("EXDBLIB",          UV_EXDBLIB);
  651. X    MAGICVAR("EXFORMS",          UV_EXFORMS);
  652. X    MAGICVAR("EXCLIPBOARD",      UV_EXCLIPBOARD);
  653. X    MAGICVAR("EXLOOKUP",         UV_EXLOOKUP);
  654. X    MAGICVAR("EXINFO",           UV_EXINFO);
  655. X    MAGICVAR("EXUSER",           UV_EXUSER);
  656. X    MAGICVAR("EXNONFATAL",       UV_EXNONFATAL);
  657. X    MAGICVAR("EXCONVERSION",     UV_EXCONVERSION);
  658. X    MAGICVAR("EXSERVER",         UV_EXSERVER);
  659. X    MAGICVAR("EXTIME",           UV_EXTIME);
  660. X    MAGICVAR("EXPROGRAM",        UV_EXPROGRAM);
  661. X    MAGICVAR("EXRESOURCE",       UV_EXRESOURCE);
  662. X    MAGICVAR("EXCOMM",           UV_EXCOMM);
  663. X    MAGICVAR("EXFATAL",          UV_EXFATAL);
  664. X    MAGICVAR("EXCONSISTENCY",    UV_EXCONSISTENCY);
  665. X#endif
  666. X    MAGICVAR("DB_IN",          UV_DB_IN);
  667. X    MAGICVAR("DB_OUT",           UV_DB_OUT);
  668. X    MAGICVAR("BCPMAXERRS",       UV_BCPMAXERRS);
  669. X    MAGICVAR("BCPFIRST",         UV_BCPFIRST);
  670. X    MAGICVAR("BCPLAST",          UV_BCPLAST);
  671. X    MAGICVAR("BCPBATCH",         UV_BCPBATCH);
  672. X    MAGICVAR("DBTRUE",           UV_DBTRUE);
  673. X    MAGICVAR("DBFALSE",          UV_DBFALSE);
  674. X#if defined(PACKAGE_BUG)
  675. X    MAGICVAR("SybPackageBug",    UV_PACKAGE_BUG);
  676. X#endif
  677. X    MAGICVAR("dbNullIsUndef",   UV_dbNullIsUndef);
  678. X    MAGICVAR("dbKeepNumeric",   UV_dbKeepNumeric);
  679. X    MAGICVAR("dbBin0x",         UV_dbBin0x);
  680. X
  681. X#if defined(PACKAGE_BUG)    
  682. X    make_usub("dbLOGIN",    US_dblogin,    usersub, filename);
  683. X    make_usub("dbOPEN",        US_dbopen,    usersub, filename);
  684. X    make_usub("dbCLOSE",    US_dbclose,    usersub, filename);
  685. X    make_usub("dbCMD",        US_dbcmd,    usersub, filename);
  686. X    make_usub("dbSQLEXEC",    US_dbsqlexec,    usersub, filename);
  687. X    make_usub("dbRESULTS",    US_dbresults,    usersub, filename);
  688. X    make_usub("dbNEXTROW",    US_dbnextrow,    usersub, filename);
  689. X    make_usub("dbCANCEL",    US_dbcancel,    usersub, filename);
  690. X    make_usub("dbCANQUERY",    US_dbcanquery,    usersub, filename);
  691. X    make_usub("dbEXIT",    US_dbexit,    usersub, filename);
  692. X    make_usub("dbUSE",    US_dbuse,    usersub, filename);
  693. X#ifdef HAS_CALLBACK
  694. X    make_usub("dbERRHANDLE", US_dberrhandle, usersub, filename);
  695. X    make_usub("dbMSGHANDLE", US_dbmsghandle, usersub, filename);
  696. X#endif
  697. X    make_usub("dbSTRCPY", US_dbstrcpy, usersub, filename);
  698. X    make_usub("dbCURCMD", US_DBCURCMD, usersub, filename);
  699. X    make_usub("dbMORECMDS", US_DBMORECMDS, usersub, filename);
  700. X    make_usub("dbCMDROW", US_DBCMDROW, usersub, filename);
  701. X    make_usub("dbROWS", US_DBROWS, usersub, filename);
  702. X    make_usub("dbCOUNT", US_DBCOUNT, usersub, filename);
  703. X    make_usub("dbHASRETSTAT", US_dbhasretstat, usersub, filename);
  704. X    make_usub("dbRETSTATUS", US_dbretstatus, usersub, filename);
  705. X    make_usub("dbRETDATA",   US_dbretdata, usersub, filename);    
  706. X    make_usub("dbWRITETEXT", US_dbwritetext, usersub, filename);
  707. X    make_usub("dbCOLTYPE",   US_dbcoltype, usersub, filename);
  708. X    make_usub("dbCOLNAME",   US_dbcolname, usersub, filename);
  709. X    make_usub("dbCOLLEN",    US_dbcollen, usersub, filename);
  710. X    make_usub("dbNUMCOLS",   US_dbnumcols, usersub, filename);
  711. X#if defined(DBLIB461)
  712. X    make_usub("dbSAFESTR",       US_dbsafestr,    usersub, filename);
  713. X    make_usub("dbMNY4ADD",       US_dbmny4add,    usersub, filename);
  714. X    make_usub("dbMNY4CMP",       US_dbmny4cmp,    usersub, filename);
  715. X    make_usub("dbMNY4DIVIDE",    US_dbmny4divide, usersub, filename);
  716. X    make_usub("dbMNY4MINUS",     US_dbmny4minus,  usersub, filename);
  717. X    make_usub("dbMNY4MUL",       US_dbmny4mul,    usersub, filename);
  718. X    make_usub("dbMNY4SUB",       US_dbmny4sub,    usersub, filename);
  719. X    make_usub("dbMNY4ZERO",      US_dbmny4zero,   usersub, filename);
  720. X    make_usub("dbMNYADD",        US_dbmnyadd,     usersub, filename);
  721. X    make_usub("dbMNYCMP",        US_dbmnycmp,     usersub, filename);
  722. X    make_usub("dbMNYDIVIDE",     US_dbmnydivide,  usersub, filename);
  723. X    make_usub("dbMNYMINUS",      US_dbmnyminus,   usersub, filename);
  724. X    make_usub("dbMNYMUL",        US_dbmnymul,     usersub, filename);
  725. X    make_usub("dbMNYSUB",        US_dbmnysub,     usersub, filename);
  726. X    make_usub("dbMNYZERO",       US_dbmnyzero,    usersub, filename);
  727. X    make_usub("dbMNYDEC",        US_dbmnydec,     usersub, filename);
  728. X    make_usub("dbMNYDOWN",       US_dbmnydown,    usersub, filename);
  729. X    make_usub("dbMNYINC",        US_dbmnyinc,     usersub, filename);
  730. X    make_usub("dbMNYINIT",       US_dbmnyinit,    usersub, filename);
  731. X    make_usub("dbMNYMAXNEG",     US_dbmnymaxneg,  usersub, filename);
  732. X    make_usub("dbMNYMAXPOS",     US_dbmnymaxpos,  usersub, filename);
  733. X    make_usub("dbMNYNDIGIT",     US_dbmnyndigit,  usersub, filename);
  734. X    make_usub("dbMNYSCALE",      US_dbmnyscale,   usersub, filename);
  735. X    make_usub("dbRECFTOS",       US_dbrecftos,    usersub, filename);
  736. X#endif
  737. X    make_usub("bcp_SETL",        US_BCP_SETL,     usersub, filename);
  738. X    make_usub("bcp_GETL",        US_bcp_getl,     usersub, filename);
  739. X    make_usub("bcp_INIT",        US_bcp_init,     usersub, filename);
  740. X    make_usub("bcp_MEMINIT",     US_bcp_meminit,  usersub, filename);
  741. X    make_usub("bcp_SENDROW",     US_bcp_sendrow,  usersub, filename);
  742. X    make_usub("bcp_BATCH",       US_bcp_batch,    usersub, filename);
  743. X    make_usub("bcp_DONE",        US_bcp_done,     usersub, filename);
  744. X    make_usub("bcp_CONTROL",     US_bcp_control,  usersub, filename);
  745. X    make_usub("bcp_COLUMNS",     US_bcp_columns,  usersub, filename);
  746. X    make_usub("bcp_COLFMT",      US_bcp_colfmt,   usersub, filename);
  747. X    make_usub("bcp_COLLEN",      US_bcp_collen,   usersub, filename);
  748. X    make_usub("bcp_EXEC",        US_bcp_exec,     usersub, filename);
  749. X    make_usub("bcp_READFMT",     US_bcp_readfmt,  usersub, filename);
  750. X    make_usub("bcp_WRITEFMT",    US_bcp_writefmt, usersub, filename);
  751. X#else
  752. X    make_usub("dblogin",    US_dblogin,    usersub, filename);
  753. X    make_usub("dbopen",        US_dbopen,    usersub, filename);
  754. X    make_usub("dbclose",    US_dbclose,    usersub, filename);
  755. X    make_usub("dbcmd",        US_dbcmd,    usersub, filename);
  756. X    make_usub("dbsqlexec",    US_dbsqlexec,    usersub, filename);
  757. X    make_usub("dbresults",    US_dbresults,    usersub, filename);
  758. X    make_usub("dbnextrow",    US_dbnextrow,    usersub, filename);
  759. X    make_usub("dbcancel",    US_dbcancel,    usersub, filename);
  760. X    make_usub("dbcanquery",    US_dbcanquery,    usersub, filename);
  761. X    make_usub("dbexit",    US_dbexit,    usersub, filename);
  762. X    make_usub("dbuse",    US_dbuse,    usersub, filename);
  763. X#ifdef HAS_CALLBACK
  764. X    make_usub("dberrhandle", US_dberrhandle, usersub, filename);
  765. X    make_usub("dbmsghandle", US_dbmsghandle, usersub, filename);
  766. X#endif
  767. X    make_usub("dbstrcpy", US_dbstrcpy, usersub, filename);
  768. X    make_usub("DBCURCMD", US_DBCURCMD, usersub, filename);
  769. X    make_usub("DBMORECMDS", US_DBMORECMDS, usersub, filename);
  770. X    make_usub("DBCMDROW", US_DBCMDROW, usersub, filename);
  771. X    make_usub("DBROWS", US_DBROWS, usersub, filename);
  772. X    make_usub("DBCOUNT", US_DBCOUNT, usersub, filename);
  773. X    make_usub("dbhasretstat", US_dbhasretstat, usersub, filename);
  774. X    make_usub("dbretstatus", US_dbretstatus, usersub, filename);
  775. X    make_usub("dbretdata", US_dbretdata, usersub, filename);
  776. X    make_usub("dbwritetext", US_dbwritetext, usersub, filename);
  777. X    make_usub("dbcoltype",   US_dbcoltype, usersub, filename);
  778. X    make_usub("dbcolname",   US_dbcolname, usersub, filename);
  779. X    make_usub("dbcollen",    US_dbcollen, usersub, filename);
  780. X    make_usub("dbnumcols",   US_dbnumcols, usersub, filename);
  781. X#if defined(DBLIB461)
  782. X    make_usub("dbsafestr",       US_dbsafestr,    usersub, filename);
  783. X    make_usub("dbmny4add",       US_dbmny4add,    usersub, filename);
  784. X    make_usub("dbmny4cmp",       US_dbmny4cmp,    usersub, filename);
  785. X    make_usub("dbmny4divide",    US_dbmny4divide, usersub, filename);
  786. X    make_usub("dbmny4minus",     US_dbmny4minus,  usersub, filename);
  787. X    make_usub("dbmny4mul",       US_dbmny4mul,    usersub, filename);
  788. X    make_usub("dbmny4sub",       US_dbmny4sub,    usersub, filename);
  789. X    make_usub("dbmny4zero",      US_dbmny4zero,   usersub, filename);
  790. X    make_usub("dbmnyadd",        US_dbmnyadd,     usersub, filename);
  791. X    make_usub("dbmnycmp",        US_dbmnycmp,     usersub, filename);
  792. X    make_usub("dbmnydivide",     US_dbmnydivide,  usersub, filename);
  793. X    make_usub("dbmnyminus",      US_dbmnyminus,   usersub, filename);
  794. X    make_usub("dbmnymul",        US_dbmnymul,     usersub, filename);
  795. X    make_usub("dbmnysub",        US_dbmnysub,     usersub, filename);
  796. X    make_usub("dbmnyzero",       US_dbmnyzero,    usersub, filename);
  797. X    make_usub("dbmnydec",        US_dbmnydec,     usersub, filename);
  798. X    make_usub("dbmnydown",       US_dbmnydown,    usersub, filename);
  799. X    make_usub("dbmnyinc",        US_dbmnyinc,     usersub, filename);
  800. X    make_usub("dbmnyinit",       US_dbmnyinit,    usersub, filename);
  801. X    make_usub("dbmnymaxneg",     US_dbmnymaxneg,  usersub, filename);
  802. X    make_usub("dbmnymaxpos",     US_dbmnymaxpos,  usersub, filename);
  803. X    make_usub("dbmnyndigit",     US_dbmnyndigit,  usersub, filename);
  804. X    make_usub("dbmnyscale",      US_dbmnyscale,   usersub, filename);
  805. X    make_usub("dbrecftos",       US_dbrecftos,    usersub, filename);
  806. X#endif
  807. X    make_usub("BCP_SETL",        US_BCP_SETL,     usersub, filename);
  808. X    make_usub("bcp_getl",        US_bcp_getl,     usersub, filename);
  809. X    make_usub("bcp_init",        US_bcp_init,     usersub, filename);
  810. X    make_usub("bcp_meminit",     US_bcp_meminit,  usersub, filename);
  811. X    make_usub("bcp_sendrow",     US_bcp_sendrow,  usersub, filename);
  812. X    make_usub("bcp_batch",       US_bcp_batch,    usersub, filename);
  813. X    make_usub("bcp_done",        US_bcp_done,     usersub, filename);
  814. X    make_usub("bcp_control",     US_bcp_control,  usersub, filename);
  815. X    make_usub("bcp_columns",     US_bcp_columns,  usersub, filename);
  816. X    make_usub("bcp_colfmt",      US_bcp_colfmt,   usersub, filename);
  817. X    make_usub("bcp_collen",      US_bcp_collen,   usersub, filename);
  818. X    make_usub("bcp_exec",        US_bcp_exec,     usersub, filename);
  819. X    make_usub("bcp_readfmt",     US_bcp_readfmt,  usersub, filename);
  820. X    make_usub("bcp_writefmt",    US_bcp_writefmt, usersub, filename);
  821. X#endif
  822. X}
  823. X
  824. Xstatic int
  825. Xusersub(ix, sp, items)
  826. Xint ix;
  827. Xregister int sp;
  828. Xregister int items;
  829. X{
  830. X    STR **st = stack->ary_array + sp;
  831. X    ARRAY *ary = stack;    
  832. X    STR *Str;        /* used in str_get and str_gnum macros */
  833. X    int inx = -1;    /* Index into dbProc[] array. Passed as */
  834. X                /* first parameter to nearly all &dbxxx() calls */
  835. X
  836. X    if(exitCalled)
  837. X    fatal("&dbexit() has been called. Access to Sybase impossible.");
  838. X
  839. X    perl_sp = sp + items;    /* Save the stack pointer - */
  840. X                /* required in the case where */
  841. X                /* callbacks are used. */
  842. X
  843. X    /* 
  844. X     * We're calling some dblib function, but dblogin has not been 
  845. X     * called. Two actions are possible: either fail the call, or call 
  846. X     * dblogin/dbopen with the default info. The second option is used 
  847. X     * to keep backwards compatibility with an older version of 
  848. X     * sybperl. A call to fatal(msg) is probably better.
  849. X     */
  850. X    if(!login && (ix != US_dblogin) &&
  851. X       (ix != US_dbmsghandle) && (ix != US_dberrhandle))
  852. X    {        /* You can call &dbmsghandle/errhandle before calling &dblogin */
  853. X#ifdef OLD_SYBPERL
  854. X    login = dblogin();
  855. X    dbProc[0].dbproc = dbopen(login, NULL);
  856. X#else
  857. X    fatal("&dblogin has not been called yet!");
  858. X#endif
  859. X    }
  860. X    
  861. X    switch (ix)
  862. X    {
  863. X      case US_dblogin:
  864. X    if (items > 3)
  865. X        fatal("Usage: &dblogin([user[,pwd[,server]]])");
  866. X    else
  867. X    {
  868. X        int j = 0;
  869. X        char *server = NULL, *user = NULL, *pwd = NULL;
  870. X
  871. X        if (!login)
  872. X        login = dblogin();
  873. X        switch(items)
  874. X        {
  875. X          case 3:
  876. X        server = (char *)str_get(STACK(sp)[3]);
  877. X          case 2:
  878. X        if(STACK(sp)[2] != &str_undef)
  879. X        {
  880. X            pwd = (char *)str_get(STACK(sp)[2]);
  881. X            if(pwd && strlen(pwd))
  882. X            DBSETLPWD(login, pwd);
  883. X        }
  884. X          case 1:
  885. X        if(STACK(sp)[1] != &str_undef)
  886. X        {
  887. X            user = (char *)str_get(STACK(sp)[1]);
  888. X            if(user && strlen(user))
  889. X            DBSETLUSER(login, user);
  890. X        }
  891. X        }
  892. X
  893. X        for(j = 0; j < MAX_DBPROCS; ++j)
  894. X        if(dbProc[j].dbproc == NULL)
  895. X            break;
  896. X        if(j == MAX_DBPROCS)
  897. X        fatal ("&dblogin: No more dbprocs available.");
  898. X        if((dbProc[j].dbproc = dbopen(login, server)) == NULL)
  899. X        j = -1;
  900. X
  901. X        str_numset(STACK(sp)[0], (double) j);
  902. X    }
  903. X    break;
  904. X      case US_dbopen:
  905. X    if (items > 1)
  906. X        fatal("Usage: $dbproc = &dbopen([server]);");
  907. X    else
  908. X    {
  909. X        int j;
  910. X        char *server = NULL;
  911. X        
  912. X        for(j = 0; j < MAX_DBPROCS; ++j)
  913. X        if(dbProc[j].dbproc == NULL)
  914. X            break;
  915. X        if(j == MAX_DBPROCS)
  916. X        fatal("&dbopen: No more dbprocs available.");
  917. X        if(items == 1)
  918. X        server = (char *)str_get(STACK(sp)[1]);
  919. X        
  920. X        dbProc[j].dbproc = dbopen(login, server);
  921. X        str_numset(STACK(sp)[0], (double) j);
  922. X    }
  923. X    break;
  924. X      case US_dbclose:
  925. X    if (items > 1)
  926. X        fatal("Usage: $ret = &dbclose($dbproc);");
  927. X    else
  928. X    {
  929. X        if(items)
  930. X        inx = getDbProc(STACK(sp)[1]);
  931. X        else
  932. X        inx = 0;
  933. X
  934. X        dbclose(dbProc[inx].dbproc);
  935. X        dbProc[inx].dbproc = (DBPROCESS *)NULL;
  936. X    }
  937. X    break;
  938. X      case US_dbcancel:
  939. X    if (items > 1)
  940. X        fatal("Usage: &dbcancel($dbproc)");
  941. X    else
  942. X    {
  943. X        int retval;
  944. X
  945. X        if(items)
  946. X        inx = getDbProc(STACK(sp)[1]);
  947. X        else
  948. X        inx = 0;
  949. X
  950. X        retval = dbcancel(dbProc[inx].dbproc);
  951. X        str_numset(STACK(sp)[0], (double) retval);
  952. X    }
  953. X    break;
  954. X
  955. X      case US_dbcanquery:
  956. X    if (items > 1)
  957. X        fatal("Usage: &dbcanquery($dbproc)");
  958. X    else
  959. X    {
  960. X        int retval;
  961. X
  962. X        if(items)
  963. X        inx = getDbProc(STACK(sp)[1]);
  964. X        else
  965. X        inx = 0;
  966. X
  967. X        retval = dbcanquery(dbProc[inx].dbproc);
  968. X        str_numset(STACK(sp)[0], (double) retval);
  969. X    }
  970. X    break;
  971. X
  972. X      case US_dbexit:
  973. X    if (items != 0)
  974. X        fatal("Usage: &dbexit()");
  975. X    else
  976. X    {
  977. X        dbexit();
  978. X        exitCalled++;
  979. X        str_numset(STACK(sp)[0], (double) 1);
  980. X    }
  981. X    break;
  982. X
  983. X      case US_dbuse:
  984. X    if (items > 2)
  985. X        fatal("Usage: &dbuse($dbproc, $database)");
  986. X    else
  987. X    {
  988. X        int retval, off;
  989. X        char str[255];
  990. X        
  991. X        if(items == 2)
  992. X        {
  993. X        inx = getDbProc(STACK(sp)[1]);
  994. X        off = 2;
  995. X        }
  996. X        else
  997. X        inx = 0, off = 1;
  998. X        
  999. X        strcpy(str, (char *)str_get(STACK(sp)[off]));
  1000. X
  1001. X
  1002. X        retval = dbuse(dbProc[inx].dbproc, str);
  1003. X        str_numset(STACK(sp)[0], (double) retval);
  1004. X    }
  1005. X    break;
  1006. X
  1007. X      case US_dbsqlexec:
  1008. X    if (items > 1)
  1009. X        fatal("Usage: &dbsqlexec($dbproc)");
  1010. X    else
  1011. X    {
  1012. X        int retval;
  1013. X        if(items)
  1014. X        inx = getDbProc(STACK(sp)[1]);
  1015. X        else
  1016. X        inx = 0;
  1017. X
  1018. X        retval = dbsqlexec(dbProc[inx].dbproc);
  1019. X        str_numset(STACK(sp)[0], (double) retval);
  1020. X    }
  1021. X    break;
  1022. X
  1023. X      case US_dbresults:
  1024. X    if (items > 1)
  1025. X        fatal("Usage: &dbresults($dbproc)");
  1026. X    else
  1027. X    {
  1028. X        int retval;
  1029. X
  1030. X        if(items)
  1031. X        inx = getDbProc(STACK(sp)[1]);
  1032. X        else
  1033. X        inx = 0;
  1034. X
  1035. X        retval = dbresults(dbProc[inx].dbproc);
  1036. X        str_numset(STACK(sp)[0], (double) retval);
  1037. X    }
  1038. X    break;
  1039. X
  1040. X      case US_dbcmd:
  1041. X    if (items > 2)
  1042. X        fatal("Usage: &dbcmd($dbproc, $str)");
  1043. X    else
  1044. X    {
  1045. X        int retval, off;
  1046. X
  1047. X        if(items == 2)
  1048. X        {
  1049. X        inx = getDbProc(STACK(sp)[1]);
  1050. X        off = 2;
  1051. X        }
  1052. X        else
  1053. X        inx = 0, off = 1;
  1054. X        retval = dbcmd(dbProc[inx].dbproc, (char *)str_get(STACK(sp)[off]));
  1055. X        str_numset(STACK(sp)[0], (double) retval);
  1056. X    }
  1057. X    break;
  1058. X
  1059. X      case US_dbnextrow:
  1060. X    if (items > 2)
  1061. X        fatal("Usage: @arr = &dbnextrow([$dbproc [, $returnAssoc]])");
  1062. X    else
  1063. X    {
  1064. X        int retval;
  1065. X        char buff[260], *p = NULL, *t;
  1066. X        BYTE *data;
  1067. X        int col, type, numcols;
  1068. X        int len;
  1069. X        int doAssoc = 0;
  1070. X        DBFLT8 tmp;
  1071. X        char *colname;
  1072. X        char cname[64];
  1073. X        int is_numeric;
  1074. X        int is_null;
  1075. X#if defined(DBLIB461)
  1076. X        DBMONEY tv_money;
  1077. X#endif
  1078. X
  1079. X        inx = 0;
  1080. X        switch(items)
  1081. X        {
  1082. X          case 2:
  1083. X        doAssoc = (int)str_gnum(STACK(sp)[2]);
  1084. X          case 1:
  1085. X        inx = getDbProc(STACK(sp)[1]);
  1086. X        break;
  1087. X        }
  1088. X
  1089. X        --sp;        /* get rid of space pre-allocation */
  1090. X
  1091. X        DBstatus = retval = dbnextrow(dbProc[inx].dbproc);
  1092. X        if(retval == REG_ROW)
  1093. X        {
  1094. X        ComputeId = 0;
  1095. X        numcols = dbnumcols(dbProc[inx].dbproc);
  1096. X        }
  1097. X        else
  1098. X        {
  1099. X        ComputeId = retval;
  1100. X        numcols = dbnumalts(dbProc[inx].dbproc, ComputeId);
  1101. X        }
  1102. X        for(col = 1, buff[0] = 0; col <= numcols; ++col)
  1103. X        {
  1104. X        is_numeric = 0;
  1105. X        is_null = 0;
  1106. X        colname = NULL;
  1107. X        if(!ComputeId)
  1108. X        {
  1109. X            type = dbcoltype(dbProc[inx].dbproc, col);
  1110. X            len = dbdatlen(dbProc[inx].dbproc,col);
  1111. X            data = (BYTE *)dbdata(dbProc[inx].dbproc,col);
  1112. X            colname = dbcolname(dbProc[inx].dbproc, col);
  1113. X            if(!colname || !colname[0])
  1114. X            {
  1115. X            sprintf(cname, "Col %d", col);
  1116. X            colname = cname;
  1117. X            }
  1118. X        }
  1119. X        else
  1120. X        {
  1121. X            int colid = dbaltcolid(dbProc[inx].dbproc, ComputeId, col);
  1122. X            type = dbalttype(dbProc[inx].dbproc, ComputeId, col);
  1123. X            len = dbadlen(dbProc[inx].dbproc, ComputeId, col);
  1124. X            data = (BYTE *)dbadata(dbProc[inx].dbproc, ComputeId, col);
  1125. X            if(colid > 0)
  1126. X            colname = dbcolname(dbProc[inx].dbproc, colid);
  1127. X            if(!colname || !colname[0])
  1128. X            {
  1129. X            sprintf(cname, "Col %d", col);
  1130. X            colname = cname;
  1131. X            }
  1132. X        }
  1133. X        t = &buff[0];
  1134. X        if(!data && !len)
  1135. X            ++is_null;
  1136. X        else
  1137. X        {
  1138. X            switch(type)
  1139. X            {
  1140. X              case SYBCHAR:
  1141. X            strncpy(buff,data,len);
  1142. X            buff[len] = 0;
  1143. X            break;
  1144. X              case SYBTEXT:
  1145. X              case SYBIMAGE:
  1146. X            New(902, p, len + 1, char);
  1147. X            memcpy(p, data, len);
  1148. X            p[len] = 0;
  1149. X            t = p;
  1150. X            break;
  1151. X              case SYBINT1:
  1152. X              case SYBBIT: /* a bit is at least a byte long... */
  1153. X            if(dbKeepNumeric)
  1154. X            {
  1155. X                tmp = *(unsigned char *)data;
  1156. X                ++is_numeric;
  1157. X            }
  1158. X            else
  1159. X                sprintf(buff,"%u",*(unsigned char *)data);
  1160. X            break;
  1161. X              case SYBINT2:
  1162. X            if(dbKeepNumeric)
  1163. X            {
  1164. X                tmp = *(short *)data;
  1165. X                ++is_numeric;
  1166. X            }
  1167. X            else
  1168. X            sprintf(buff,"%d",*(short *)data);
  1169. X            break;
  1170. X              case SYBINT4:
  1171. X            if(dbKeepNumeric)
  1172. X            {
  1173. X                tmp = *(long *)data;
  1174. X                ++is_numeric;
  1175. X            }
  1176. X            else
  1177. X                sprintf(buff,"%d",*(long *)data);
  1178. X            break;
  1179. X              case SYBFLT8:
  1180. X            if(dbKeepNumeric)
  1181. X            {
  1182. X                tmp = *(double *)data;
  1183. X                ++is_numeric;
  1184. X            }
  1185. X            else
  1186. X                sprintf(buff,"%.6f",*(double *)data);
  1187. X            break;
  1188. X#if   defined(DBLIB461)
  1189. X              case SYBMONEY:
  1190. X            dbconvert(dbProc[inx].dbproc, SYBMONEY, data, len,
  1191. X                  SYBMONEY, (BYTE*)&tv_money, -1);
  1192. X            new_mnytochar(dbProc[inx].dbproc, &tv_money, buff);
  1193. X            break;
  1194. X#else
  1195. X              case SYBMONEY:
  1196. X            dbconvert(dbProc[inx].dbproc, SYBMONEY, data, len,
  1197. X                  SYBFLT8, &tmp, -1);
  1198. X            if(dbKeepNumeric)
  1199. X                ++is_numeric;
  1200. X            else
  1201. X                sprintf(buff,"%.6f",tmp);
  1202. X            break;
  1203. X#endif
  1204. X              case SYBDATETIME:
  1205. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME, data, len,
  1206. X                  SYBCHAR, buff, -1);
  1207. X            break;
  1208. X              case SYBBINARY:
  1209. X            if(dbBin0x)
  1210. X            {
  1211. X                strcpy(buff, "0x");
  1212. X                dbconvert(dbProc[inx].dbproc, type, data, len,
  1213. X                      SYBCHAR, &buff[2], -1);
  1214. X            }
  1215. X            else
  1216. X                dbconvert(dbProc[inx].dbproc, type, data, len,
  1217. X                      SYBCHAR, buff, -1);
  1218. X            break;
  1219. X#if defined(DBLIB42)
  1220. X              case SYBREAL:
  1221. X            if(dbKeepNumeric)
  1222. X            {
  1223. X                tmp = *(float *)data;
  1224. X                ++is_numeric;
  1225. X            }
  1226. X            else
  1227. X                sprintf(buff, "%.6f", (double)*(float *)data);
  1228. X            break;
  1229. X              case SYBDATETIME4:
  1230. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME4, data, len,
  1231. X                  SYBCHAR, buff, -1);
  1232. X            break;
  1233. X#elif defined(DBLIB461)
  1234. X              case SYBREAL:
  1235. X            if(dbKeepNumeric)
  1236. X            {
  1237. X                tmp = *(float *)data;
  1238. X                ++is_numeric;
  1239. X            }
  1240. X            else
  1241. X                sprintf(buff, "%.6f", (double)*(float *)data);
  1242. X            break;
  1243. X              case SYBDATETIME4:
  1244. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME4, data, len,
  1245. X                  SYBCHAR, buff, -1);
  1246. X            break;
  1247. X              case SYBMONEY4:
  1248. X            dbconvert(dbProc[inx].dbproc, SYBMONEY4, data, len,
  1249. X                  SYBMONEY, (BYTE*)&tv_money, -1);
  1250. X            new_mnytochar(dbProc[inx].dbproc, &tv_money, buff);
  1251. X            break;
  1252. X#endif
  1253. X              default:
  1254. X            /* 
  1255. X             * WARNING!
  1256. X             * 
  1257. X             * We convert unknown data types to SYBCHAR 
  1258. X             * without checking to see if the resulting 
  1259. X             * string will fit in the 'buff' variable. 
  1260. X             * This isn't very pretty...
  1261. X             */
  1262. X            dbconvert(dbProc[inx].dbproc, type, data, len,
  1263. X                  SYBCHAR, buff, -1);
  1264. X            break;
  1265. X            }
  1266. X        }
  1267. X        if(doAssoc)
  1268. X            (void)astore(ary,++sp,str_2mortal(str_make(colname, 0)));
  1269. X        if(type != SYBIMAGE)
  1270. X            len = 0;    /* str_make needs to know the lenght only on binary data */
  1271. X        if(is_null)
  1272. X        {
  1273. X            if(dbNullIsUndef)
  1274. X            {
  1275. X            /* we make a copy of str_undef to be on the safe */
  1276. X            /* side (we don't want somebody modifying it! */
  1277. X            (void)astore(ary,++sp,str_mortal(&str_undef));
  1278. X            continue; /* whatever follows here (in this iteration) is irrelevant */
  1279. X                  /* when NULLs are returned as undef */
  1280. X            }
  1281. X            else
  1282. X            strcpy(buff,"NULL");
  1283. X        }
  1284. X        if(is_numeric)
  1285. X            (void)astore(ary,++sp,str_2mortal(str_nmake(tmp)));
  1286. X        else
  1287. X            (void)astore(ary,++sp,str_2mortal(str_make(t, len)));
  1288. X        /* 
  1289. X         * If we've allocated some space to retrieve a 
  1290. X         * SYBTEXT field, then free it now.
  1291. X         */
  1292. X        if(t == p)
  1293. X        {
  1294. X            Safefree(p);
  1295. X            p = NULL;
  1296. X        }
  1297. X        }
  1298. X    }
  1299. X    break;
  1300. X#ifdef HAS_CALLBACK
  1301. X      case US_dberrhandle:
  1302. X    if (items > 1)
  1303. X        fatal ("Usage: &dberrhandle($handler)");
  1304. X    else
  1305. X    {
  1306. X        char *old = err_handler_sub;
  1307. X        if (items == 1)
  1308. X        {
  1309. X        if (STACK (sp)[1] == &str_undef)
  1310. X            err_handler_sub = 0;
  1311. X        else
  1312. X        {
  1313. X            char *sub = (char *) str_get (STACK (sp)[1]);    
  1314. X            New (902, err_handler_sub, strlen (sub) + 1, char);
  1315. X            strcpy (err_handler_sub, sub);
  1316. X        }
  1317. X        }
  1318. X
  1319. X        if (old)
  1320. X        {
  1321. X        STACK (sp)[0] = str_2mortal (str_make (old, strlen (old)));
  1322. X        if (items == 1)
  1323. X            Safefree (old);
  1324. X        }
  1325. X        else
  1326. X        STACK (sp)[0] = &str_undef;
  1327. X    }
  1328. X    break;
  1329. X      case US_dbmsghandle:
  1330. X    if (items > 1)
  1331. X        fatal ("Usage: &dbmsghandle($handler)");
  1332. X    else
  1333. X    {
  1334. X        char *old = msg_handler_sub;
  1335. X        if (items == 1)
  1336. X        {
  1337. X        if (STACK (sp)[1] == &str_undef)
  1338. X            msg_handler_sub = 0;
  1339. X        else
  1340. X        {
  1341. X            char *sub = (char *) str_get (STACK (sp)[1]);    
  1342. X            New (902, msg_handler_sub, strlen (sub) + 1, char);
  1343. X            strcpy (msg_handler_sub, sub);
  1344. X        }
  1345. X        }
  1346. X
  1347. X        if (old)
  1348. X        {
  1349. X        STACK (sp)[0] = str_2mortal (str_make (old, strlen (old)));
  1350. X        if (items == 1)
  1351. X            Safefree (old);
  1352. X        }
  1353. X        else
  1354. X        STACK (sp)[0] = &str_undef;
  1355. X    }
  1356. X    break;
  1357. X#endif                /* HAS_CALLBACK */
  1358. X      case US_dbstrcpy:
  1359. X    if (items > 1)
  1360. X        fatal("Usage: $string = &dbstrcpy($dbproc)");
  1361. X    else
  1362. X    {
  1363. X        int retval, len;
  1364. X        char *buff;
  1365. X
  1366. X        if(items)
  1367. X        inx = getDbProc(STACK(sp)[1]);
  1368. X        else
  1369. X        inx = 0;
  1370. X
  1371. X        if(dbProc[inx].dbproc && (len = dbstrlen(dbProc[inx].dbproc)))
  1372. X        {
  1373. X        New(902, buff, len+1, char);
  1374. X        retval = dbstrcpy(dbProc[inx].dbproc, 0, -1, buff);
  1375. X        str_set(STACK(sp)[0], buff);
  1376. X        Safefree(buff);
  1377. X        }
  1378. X        else
  1379. X        str_set(STACK(sp)[0], "");
  1380. X    }
  1381. X    break;
  1382. X
  1383. X      case US_DBCURCMD:
  1384. X    if (items > 1)
  1385. X        fatal("Usage: $num = &DBCURCMD($dbproc)");
  1386. X    else
  1387. X    {
  1388. X        int retval = 0;
  1389. X
  1390. X        if(items)
  1391. X        inx = getDbProc(STACK(sp)[1]);
  1392. X        else
  1393. X        inx = 0;
  1394. X
  1395. X        if(dbProc[inx].dbproc)
  1396. X        retval = DBCURCMD(dbProc[inx].dbproc);
  1397. X
  1398. X        str_numset(STACK(sp)[0], (double) retval);
  1399. X    }
  1400. X    break;
  1401. X      case US_DBMORECMDS:
  1402. X    if (items > 1)
  1403. X        fatal("Usage: $rc = &DBMORECMDS($dbproc)");
  1404. X    else
  1405. X    {
  1406. X        int retval = 0;
  1407. X
  1408. X        if(items)
  1409. X        inx = getDbProc(STACK(sp)[1]);
  1410. X        else
  1411. X        inx = 0;
  1412. X
  1413. X        if(dbProc[inx].dbproc)
  1414. X        retval = DBMORECMDS(dbProc[inx].dbproc);
  1415. X
  1416. X        str_numset(STACK(sp)[0], (double) retval);
  1417. X    }
  1418. X    break;
  1419. X      case US_DBCMDROW:
  1420. X    if (items > 1)
  1421. X        fatal("Usage: $rc = &DBCMDROW($dbproc)");
  1422. X    else
  1423. X    {
  1424. X        int retval = 0;
  1425. X
  1426. X        if(items)
  1427. X        inx = getDbProc(STACK(sp)[1]);
  1428. X        else
  1429. X        inx = 0;
  1430. X
  1431. X        if(dbProc[inx].dbproc)
  1432. X        retval = DBCMDROW(dbProc[inx].dbproc);
  1433. X
  1434. X        str_numset(STACK(sp)[0], (double) retval);
  1435. X    }
  1436. X    break;
  1437. X      case US_DBROWS:
  1438. X    if (items > 1)
  1439. X        fatal("Usage: $rc = &DBROWS($dbproc)");
  1440. X    else
  1441. X    {
  1442. X        int retval = 0;
  1443. X
  1444. X        if(items)
  1445. X        inx = getDbProc(STACK(sp)[1]);
  1446. X        else
  1447. X        inx = 0;
  1448. X
  1449. X        if(dbProc[inx].dbproc)
  1450. X        retval = DBROWS(dbProc[inx].dbproc);
  1451. X
  1452. X        str_numset(STACK(sp)[0], (double) retval);
  1453. X    }
  1454. X    break;
  1455. X      case US_DBCOUNT:
  1456. X    if (items > 1)
  1457. X        fatal("Usage: $ret = &DBCOUNT($dbproc)");
  1458. X    else
  1459. X    {
  1460. X        int retval = 0;
  1461. X
  1462. X        if(items)
  1463. X        inx = getDbProc(STACK(sp)[1]);
  1464. X        else
  1465. X        inx = 0;
  1466. X
  1467. X        if(dbProc[inx].dbproc)
  1468. X        retval = DBCOUNT(dbProc[inx].dbproc);
  1469. X
  1470. X        str_numset(STACK(sp)[0], (double) retval);
  1471. X    }
  1472. X    break;
  1473. X      case US_dbhasretstat:
  1474. X    if (items > 1)
  1475. X        fatal("Usage: $rc = &dbhasretstat($dbproc)");
  1476. X    else
  1477. X    {
  1478. X        int retval = 0;
  1479. X
  1480. X        if(items)
  1481. X        inx = getDbProc(STACK(sp)[1]);
  1482. X        else
  1483. X        inx = 0;
  1484. X
  1485. X        if(dbProc[inx].dbproc)
  1486. X        retval = dbhasretstat(dbProc[inx].dbproc);
  1487. X
  1488. X        str_numset(STACK(sp)[0], (double) retval);
  1489. X    }
  1490. X    break;
  1491. X      case US_dbretstatus:
  1492. X    if (items > 1)
  1493. X        fatal("Usage: $rc = &dbretstatus($dbproc)");
  1494. X    else
  1495. X    {
  1496. X        int retval = 0;
  1497. X
  1498. X        if(items)
  1499. X        inx = getDbProc(STACK(sp)[1]);
  1500. X        else
  1501. X        inx = 0;
  1502. X
  1503. X        if(dbProc[inx].dbproc)
  1504. X        retval = dbretstatus(dbProc[inx].dbproc);
  1505. X
  1506. X        str_numset(STACK(sp)[0], (double) retval);
  1507. X    }
  1508. X    break;
  1509. X      case US_dbretdata:
  1510. X    if (items > 2)
  1511. X        fatal("Usage: @data = &dbretdata($dbproc [, $doAssoc])");
  1512. X    else
  1513. X    {
  1514. X        int numrets;
  1515. X        int retval;
  1516. X        char buff[260], *p = NULL, *t;
  1517. X        BYTE *data;
  1518. X        int col, type;
  1519. X        int len;
  1520. X        int doAssoc = 0;
  1521. X        DBFLT8 tmp;
  1522. X        char *colname;
  1523. X        char cname[64];
  1524. X        int is_numeric;
  1525. X        int is_null;
  1526. X#if defined(DBLIB461)
  1527. X        DBMONEY tv_money;
  1528. X#endif
  1529. X        
  1530. X        if(items == 2)
  1531. X        {
  1532. X        inx = getDbProc(STACK(sp)[1]);
  1533. X        doAssoc = str_gnum(STACK(sp)[2]);
  1534. X        }
  1535. X        else
  1536. X        inx = 0;
  1537. X        --sp;        /* get rid of space pre-allocation */
  1538. X        
  1539. X        if(!(numrets = dbnumrets(dbProc[inx].dbproc)))
  1540. X        break;        /* nothing to return! */
  1541. X
  1542. X        for(col = 1, buff[0] = 0; col <= numrets; ++col)
  1543. X        {
  1544. X        is_numeric = 0;
  1545. X        is_null = 0;
  1546. X        colname = NULL;
  1547. X        type = dbrettype(dbProc[inx].dbproc, col);
  1548. X        len = dbretlen(dbProc[inx].dbproc,col);
  1549. X        data = (BYTE *)dbretdata(dbProc[inx].dbproc,col);
  1550. X        colname = dbretname(dbProc[inx].dbproc, col);
  1551. X        if(!colname || !colname[0])
  1552. X        {
  1553. X            sprintf(cname, "Par %d", col);
  1554. X            colname = cname;
  1555. X        }
  1556. X        t = &buff[0];
  1557. X        if(!data && !len)
  1558. X            ++is_null;
  1559. X        else
  1560. X        {
  1561. X            switch(type)
  1562. X            {
  1563. X              case SYBCHAR:
  1564. X            strncpy(buff,data,len);
  1565. X            buff[len] = 0;
  1566. X            break;
  1567. X              case SYBTEXT:
  1568. X              case SYBIMAGE:
  1569. X            New(902, p, len + 1, char);
  1570. X            memcpy(p, data, len);
  1571. X            p[len] = 0;
  1572. X            t = p;
  1573. X            break;
  1574. X              case SYBINT1:
  1575. X              case SYBBIT: /* a bit is at least a byte long... */
  1576. X            if(dbKeepNumeric)
  1577. X            {
  1578. X                tmp = *(unsigned char *)data;
  1579. X                ++is_numeric;
  1580. X            }
  1581. X            else
  1582. X                sprintf(buff,"%u",*(unsigned char *)data);
  1583. X            break;
  1584. X              case SYBINT2:
  1585. X            if(dbKeepNumeric)
  1586. X            {
  1587. X                tmp = *(short *)data;
  1588. X                ++is_numeric;
  1589. X            }
  1590. X            else
  1591. X                sprintf(buff,"%d",*(short *)data);
  1592. X            break;
  1593. X              case SYBINT4:
  1594. X            if(dbKeepNumeric)
  1595. X            {
  1596. X                tmp = *(long *)data;
  1597. X                ++is_numeric;
  1598. X            }
  1599. X            else
  1600. X                sprintf(buff,"%d",*(long *)data);
  1601. X            break;
  1602. X              case SYBFLT8:
  1603. X            if(dbKeepNumeric)
  1604. X            {
  1605. X                tmp = *(double *)data;
  1606. X                ++is_numeric;
  1607. X            }
  1608. X            else
  1609. X                sprintf(buff,"%.6f",*(double *)data);
  1610. X            break;
  1611. X#if   defined(DBLIB461)
  1612. X              case SYBMONEY:
  1613. X            dbconvert(dbProc[inx].dbproc, SYBMONEY, data, len,
  1614. X                  SYBMONEY, (BYTE*)&tv_money, -1);
  1615. X            new_mnytochar(dbProc[inx].dbproc, &tv_money, buff);
  1616. X            break;
  1617. X#else
  1618. X              case SYBMONEY:
  1619. X            dbconvert(dbProc[inx].dbproc, SYBMONEY, data, len,
  1620. X                  SYBFLT8, &tmp, -1);
  1621. X            if(dbKeepNumeric)
  1622. X                ++is_numeric;
  1623. X            else
  1624. X                sprintf(buff,"%.6f",tmp);
  1625. X            break;
  1626. X#endif
  1627. X              case SYBDATETIME:
  1628. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME, data, len,
  1629. X                  SYBCHAR, buff, -1);
  1630. X            break;
  1631. X              case SYBBINARY:
  1632. X            if(dbBin0x)
  1633. X            {
  1634. X                strcpy(buff, "0x");
  1635. X                dbconvert(dbProc[inx].dbproc, type, data, len,
  1636. X                      SYBCHAR, &buff[2], -1);
  1637. X            }
  1638. X            else
  1639. X                dbconvert(dbProc[inx].dbproc, type, data, len,
  1640. X                      SYBCHAR, buff, -1);
  1641. X            break;
  1642. X#if defined(DBLIB42)
  1643. X              case SYBREAL:
  1644. X            if(dbKeepNumeric)
  1645. X            {
  1646. X                tmp = *(float *)data;
  1647. X                ++is_numeric;
  1648. X            }
  1649. X            else
  1650. X                sprintf(buff, "%.6f", (double)*(float *)data);
  1651. X            break;
  1652. X              case SYBDATETIME4:
  1653. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME4, data, len,
  1654. X                  SYBCHAR, buff, -1);
  1655. X            break;
  1656. X#elif defined(DBLIB461)
  1657. X              case SYBREAL:
  1658. X            if(dbKeepNumeric)
  1659. X            {
  1660. X                tmp = *(float *)data;
  1661. X                ++is_numeric;
  1662. X            }
  1663. X            else
  1664. X                sprintf(buff, "%.6f", (double)*(float *)data);
  1665. X            break;
  1666. X              case SYBDATETIME4:
  1667. X            dbconvert(dbProc[inx].dbproc, SYBDATETIME4, data, len,
  1668. X                  SYBCHAR, buff, -1);
  1669. X            break;
  1670. X              case SYBMONEY4:
  1671. X            dbconvert(dbProc[inx].dbproc, SYBMONEY4, data, len,
  1672. X                  SYBMONEY, (BYTE*)&tv_money, -1);
  1673. X            new_mnytochar(dbProc[inx].dbproc, &tv_money, buff);
  1674. X            break;
  1675. X#endif
  1676. X              default:
  1677. X            /* 
  1678. X             * WARNING!
  1679. X             * 
  1680. X             * We convert unknown data types to SYBCHAR 
  1681. X             * without checking to see if the resulting 
  1682. X             * string will fit in the 'buff' variable. 
  1683. X             * This isn't very pretty...
  1684. X             */
  1685. X            dbconvert(dbProc[inx].dbproc, type, data, len,
  1686. X                  SYBCHAR, buff, -1);
  1687. X            break;
  1688. X            }
  1689. X        }
  1690. X        if(doAssoc)
  1691. X            (void)astore(ary,++sp,str_2mortal(str_make(colname, 0)));
  1692. X        if(type != SYBIMAGE)
  1693. X            len = 0;    /* str_make needs to know the lenght only on binary data */
  1694. X        if(is_null)
  1695. X        {
  1696. X            if(dbNullIsUndef)
  1697. X            {
  1698. X            (void)astore(ary,++sp,str_mortal(&str_undef));
  1699. X            continue; /* skip the rest of the processing */
  1700. X                  /* in this iteration */
  1701. X            }
  1702. X            else
  1703. X            strcpy(buff,"NULL");
  1704. X        }
  1705. X        if(is_numeric)
  1706. X            (void)astore(ary,++sp,str_2mortal(str_nmake(tmp)));
  1707. X        else
  1708. X            (void)astore(ary,++sp,str_2mortal(str_make(t, len)));
  1709. X        /* 
  1710. X         * If we've allocated some space to retrieve a 
  1711. X         * SYBTEXT field, then free it now.
  1712. X         */
  1713. X        if(t == p)
  1714. X        {
  1715. X            Safefree(p);
  1716. X            p = NULL;
  1717. X        }
  1718. X        }
  1719. X    }
  1720. X    break;
  1721. X#if defined(DBLIB42)
  1722. X      case US_dbsafestr:
  1723. X    if (items > 3 || items != 2)
  1724. X        fatal ("Usage: $string = &dbsafestr($dbproc,$instring[,$quote_char])");
  1725. X    else
  1726. X    {
  1727. X        int retval, len, quote;
  1728. X        char *buff, *instr;
  1729. X        
  1730. X        inx = getDbProc (STACK (sp)[1]);
  1731. X        
  1732. X        instr = (char *) str_get (STACK (sp)[2]);
  1733. X        if (items != 3)
  1734. X        quote = DBBOTH;
  1735. X        else
  1736. X        {
  1737. X        char *quote_char = (char *) str_get (STACK (sp)[3]);
  1738. X        if (*quote_char == '\"')
  1739. X            quote = DBDOUBLE;
  1740. X        else if (*quote_char == '\'')
  1741. X            quote = DBSINGLE;
  1742. X        else
  1743. X        { /* invalid  */
  1744. X            str_set (STACK (sp)[0], "");
  1745. X            break;
  1746. X        }
  1747. X        }
  1748. X        if (dbProc[inx].dbproc && (len = strlen (instr)))
  1749. X        {
  1750. X        /* twice as much space needed worst case */
  1751. X        New (902, buff, len * 2 + 1, char);
  1752. X        retval = dbsafestr (dbProc[inx].dbproc, instr, -1, buff, -1, quote);
  1753. X                str_set (STACK (sp)[0], buff);
  1754. X                Safefree (buff);
  1755. X        }
  1756. X    }
  1757. X    break;
  1758. X#elif defined(DBLIB461)
  1759. X      case US_dbsafestr:
  1760. X    if (items > 3 || items != 2)
  1761. X        fatal ("Usage: $string = &dbsafestr($dbproc,$instring[,$quote_char])");
  1762. X    else
  1763. X    {
  1764. X        int retval, len, quote;
  1765. X        char *buff, *instr;
  1766. X        
  1767. X        inx = getDbProc (STACK (sp)[1]);
  1768. X        
  1769. X        instr = (char *) str_get (STACK (sp)[2]);
  1770. X        if (items != 3)
  1771. X        quote = DBBOTH;
  1772. X        else
  1773. X        {
  1774. X        char *quote_char = (char *) str_get (STACK (sp)[3]);
  1775. X        if (*quote_char == '\"')
  1776. X            quote = DBDOUBLE;
  1777. X        else if (*quote_char == '\'')
  1778. X            quote = DBSINGLE;
  1779. X        else
  1780. X        { /* invalid  */
  1781. X            str_set (STACK (sp)[0], "");
  1782. X            break;
  1783. X        }
  1784. X        }
  1785. X        if (dbProc[inx].dbproc && (len = strlen (instr)))
  1786. X        {
  1787. X        /* twice as much space needed worst case */
  1788. X        New (902, buff, len * 2 + 1, char);
  1789. X        retval = dbsafestr (dbProc[inx].dbproc, instr, -1, buff, -1, quote);
  1790. X                str_set (STACK (sp)[0], buff);
  1791. X                Safefree (buff);
  1792. X        }
  1793. X    }
  1794. X    break;
  1795. X      case US_dbmny4add:
  1796. X    if ((items > 3) || (items < 2 ))
  1797. X        {
  1798. X        fatal("Usage: @arr = &dbmny4add($dbproc, $m1, $m2)");
  1799. X        }
  1800. X    else
  1801. X    {
  1802. X        int      retval, off1, off2;
  1803. X        DBMONEY4 m1, m2, mresult;
  1804. X            DBCHAR   mnybuf[40];
  1805. X
  1806. X        if(items == 3)
  1807. X        {
  1808. X        inx  = getDbProc(STACK(sp)[1]);
  1809. X        off1 = 2;
  1810. X        off2 = 3;
  1811. X        }
  1812. X        else
  1813. X        {
  1814. X        inx  = 0;
  1815. X        off1 = 1;
  1816. X        off2 = 2;
  1817. X            }
  1818. X
  1819. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1820. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  1821. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  1822. X            {
  1823. X           fatal("Invalid dbconvert() for &dbmny4add $m1 parameter");
  1824. X            }
  1825. X
  1826. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1827. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  1828. X              SYBMONEY4, (BYTE*)&m2, (DBINT)-1) == -1)
  1829. X            {
  1830. X           fatal("Invalid dbconvert() for &dbmny4add $m2 parameter");
  1831. X            }
  1832. X
  1833. X        retval = dbmny4add(dbProc[inx].dbproc, &m1, &m2, &mresult);
  1834. X
  1835. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  1836. X
  1837. X            --sp;  /* readjust to get rid of space preallocation */
  1838. X
  1839. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  1840. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  1841. X    }
  1842. X    break;
  1843. X      case US_dbmny4cmp:
  1844. X    if ((items > 3) || (items < 2 ))
  1845. X        {
  1846. X        fatal("Usage: &dbmny4cmp($dbproc, $m1, $m2)");
  1847. X        }
  1848. X    else
  1849. X    {
  1850. X        int      retval, off1, off2;
  1851. X        DBMONEY4 m1, m2;
  1852. X
  1853. X        if(items == 3)
  1854. X        {
  1855. X        inx  = getDbProc(STACK(sp)[1]);
  1856. X        off1 = 2;
  1857. X        off2 = 3;
  1858. X        }
  1859. X        else
  1860. X        {
  1861. X        inx  = 0;
  1862. X        off1 = 1;
  1863. X        off2 = 2;
  1864. X            }
  1865. X
  1866. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1867. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  1868. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  1869. X            {
  1870. X           fatal("Invalid dbconvert() for &dbmny4cmp $m1 parameter");
  1871. X            }
  1872. X
  1873. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1874. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  1875. X              SYBMONEY4, (BYTE*)&m2, (DBINT)-1) == -1)
  1876. X            {
  1877. X           fatal("Invalid dbconvert() for &dbmny4cmp $m2 parameter");
  1878. X            }
  1879. X
  1880. X        retval = dbmny4cmp(dbProc[inx].dbproc, &m1, &m2);
  1881. X
  1882. X        str_numset(STACK(sp)[0], (double)retval);
  1883. X    }
  1884. X    break;
  1885. X      case US_dbmny4divide:
  1886. X    if ((items > 3) || (items < 2 ))
  1887. X        {
  1888. X        fatal("Usage: @arr = &dbmny4divide($dbproc, $m1, $m2)");
  1889. X        }
  1890. X    else
  1891. X    {
  1892. X        int      retval, off1, off2;
  1893. X        DBMONEY4 m1, m2, mresult;
  1894. X            DBCHAR   mnybuf[40];
  1895. X
  1896. X        if(items == 3)
  1897. X        {
  1898. X        inx  = getDbProc(STACK(sp)[1]);
  1899. X        off1 = 2;
  1900. X        off2 = 3;
  1901. X        }
  1902. X        else
  1903. X        {
  1904. X        inx  = 0;
  1905. X        off1 = 1;
  1906. X        off2 = 2;
  1907. X            }
  1908. X
  1909. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1910. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  1911. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  1912. X            {
  1913. X           fatal("Invalid dbconvert() for &dbmny4divide $m1 parameter");
  1914. X            }
  1915. X
  1916. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1917. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  1918. X              SYBMONEY4, (BYTE*)&m2, (DBINT)-1) == -1)
  1919. X            {
  1920. X           fatal("Invalid dbconvert() for &dbmny4divide $m2 parameter");
  1921. X            }
  1922. X
  1923. X        retval = dbmny4divide(dbProc[inx].dbproc, &m1, &m2, &mresult);
  1924. X
  1925. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  1926. X
  1927. X            --sp;  /* readjust to get rid of space preallocation */
  1928. X
  1929. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  1930. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  1931. X    }
  1932. X    break;
  1933. X      case US_dbmny4minus:
  1934. X    if ((items > 2) || (items < 1 ))
  1935. X        {
  1936. X        fatal("Usage: @arr = &dbmny4minus($dbproc, $m1)");
  1937. X        }
  1938. X    else
  1939. X    {
  1940. X        int      retval, off1;
  1941. X        DBMONEY4 m1, mresult;
  1942. X            DBCHAR   mnybuf[40];
  1943. X
  1944. X        if(items == 2)
  1945. X        {
  1946. X        inx  = getDbProc(STACK(sp)[1]);
  1947. X        off1 = 2;
  1948. X        }
  1949. X        else
  1950. X        {
  1951. X        inx  = 0;
  1952. X        off1 = 1;
  1953. X            }
  1954. X
  1955. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1956. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  1957. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  1958. X            {
  1959. X           fatal("Invalid dbconvert() for &dbmny4minus $m1 parameter");
  1960. X            }
  1961. X
  1962. X        retval = dbmny4minus(dbProc[inx].dbproc, &m1, &mresult);
  1963. X
  1964. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  1965. X
  1966. X            --sp;  /* readjust to get rid of space preallocation */
  1967. X
  1968. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  1969. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  1970. X    }
  1971. X    break;
  1972. X      case US_dbmny4mul:
  1973. X    if ((items > 3) || (items < 2 ))
  1974. X        {
  1975. X        fatal("Usage: @arr = &dbmny4mul($dbproc, $m1, $m2)");
  1976. X        }
  1977. X    else
  1978. X    {
  1979. X        int      retval, off1, off2;
  1980. X        DBMONEY4 m1, m2, mresult;
  1981. X            DBMONEY  tv_money;
  1982. X            DBCHAR   mnybuf[40];
  1983. X
  1984. X        if(items == 3)
  1985. X        {
  1986. X        inx  = getDbProc(STACK(sp)[1]);
  1987. X        off1 = 2;
  1988. X        off2 = 3;
  1989. X        }
  1990. X        else
  1991. X        {
  1992. X        inx  = 0;
  1993. X        off1 = 1;
  1994. X        off2 = 2;
  1995. X            }
  1996. X
  1997. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  1998. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  1999. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  2000. X            {
  2001. X           fatal("Invalid dbconvert() for &dbmny4mul $m1 parameter");
  2002. X            }
  2003. X
  2004. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  2005. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  2006. X              SYBMONEY4, (BYTE*)&m2, (DBINT)-1) == -1)
  2007. X            {
  2008. X           fatal("Invalid dbconvert() for &dbmny4mul $m2 parameter");
  2009. X            }
  2010. X
  2011. X        retval = dbmny4mul(dbProc[inx].dbproc, &m1, &m2, &mresult);
  2012. X
  2013. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  2014. X
  2015. X            --sp;  /* readjust to get rid of space preallocation */
  2016. X
  2017. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  2018. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  2019. X    }
  2020. X    break;
  2021. X      case US_dbmny4sub:
  2022. X    if ((items > 3) || (items < 2 ))
  2023. X        {
  2024. X        fatal("Usage: @arr = &dbmny4sub($dbproc, $m1, $m2)");
  2025. X        }
  2026. X    else
  2027. X    {
  2028. X        int      retval, off1, off2;
  2029. X        DBMONEY4 m1, m2, mresult;
  2030. X            DBCHAR   mnybuf[40];
  2031. X
  2032. X        if(items == 3)
  2033. X        {
  2034. X        inx  = getDbProc(STACK(sp)[1]);
  2035. X        off1 = 2;
  2036. X        off2 = 3;
  2037. X        }
  2038. X        else
  2039. X        {
  2040. X        inx  = 0;
  2041. X        off1 = 1;
  2042. X        off2 = 2;
  2043. X            }
  2044. X
  2045. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  2046. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  2047. X              SYBMONEY4, (BYTE*)&m1, (DBINT)-1) == -1)
  2048. X            {
  2049. X           fatal("Invalid dbconvert() for &dbmny4sub $m1 parameter");
  2050. X            }
  2051. X
  2052. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  2053. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  2054. X              SYBMONEY4, (BYTE*)&m2, (DBINT)-1) == -1)
  2055. X            {
  2056. X           fatal("Invalid dbconvert() for &dbmny4sub $m2 parameter");
  2057. X            }
  2058. X
  2059. X        retval = dbmny4sub(dbProc[inx].dbproc, &m1, &m2, &mresult);
  2060. X
  2061. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  2062. X
  2063. X            --sp;  /* readjust to get rid of space preallocation */
  2064. X
  2065. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  2066. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  2067. X    }
  2068. X    break;
  2069. X      case US_dbmny4zero:
  2070. X    if (items > 1)
  2071. X        {
  2072. X        fatal("Usage: @arr = &dbmny4zero($dbproc)");
  2073. X        }
  2074. X    else
  2075. X    {
  2076. X        int      retval;
  2077. X        DBMONEY4 mresult;
  2078. X            DBMONEY  tv_money;
  2079. X            DBCHAR   mnybuf[40];
  2080. X
  2081. X        if(items == 1)
  2082. X        {
  2083. X        inx = getDbProc(STACK(sp)[1]);
  2084. X        }
  2085. X        else
  2086. X        {
  2087. X        inx = 0;
  2088. X            }
  2089. X
  2090. X        retval = dbmny4zero(dbProc[inx].dbproc, &mresult);
  2091. X
  2092. X            new_mny4tochar(dbProc[inx].dbproc, &mresult, mnybuf);
  2093. X
  2094. X            --sp;  /* readjust to get rid of space preallocation */
  2095. X
  2096. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  2097. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  2098. X    }
  2099. X    break;
  2100. X      case US_dbmnyadd:
  2101. X    if ((items > 3) || (items < 2 ))
  2102. X        {
  2103. X        fatal("Usage: @arr = &dbmnyadd($dbproc, $m1, $m2)");
  2104. X        }
  2105. X    else
  2106. X    {
  2107. X        int     retval, off1, off2;
  2108. X        DBMONEY m1, m2, mresult;
  2109. X            DBCHAR  mnybuf[40];
  2110. X
  2111. X        if(items == 3)
  2112. X        {
  2113. X        inx  = getDbProc(STACK(sp)[1]);
  2114. X        off1 = 2;
  2115. X        off2 = 3;
  2116. X        }
  2117. X        else
  2118. X        {
  2119. X        inx  = 0;
  2120. X        off1 = 1;
  2121. X        off2 = 2;
  2122. X            }
  2123. X
  2124. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  2125. X              (char *)str_get(STACK(sp)[off1]), (DBINT)-1,
  2126. X              SYBMONEY, (BYTE*)&m1, (DBINT)-1) == -1)
  2127. X            {
  2128. X           fatal("Invalid dbconvert() for &dbmnyadd $m1 parameter");
  2129. X            }
  2130. X
  2131. X        if (dbconvert(dbProc[inx].dbproc, SYBCHAR,
  2132. X              (char *)str_get(STACK(sp)[off2]), (DBINT)-1,
  2133. X              SYBMONEY, (BYTE*)&m2, (DBINT)-1) == -1)
  2134. X            {
  2135. X           fatal("Invalid dbconvert() for &dbmnyadd $m2 parameter");
  2136. X            }
  2137. X
  2138. X        retval = dbmnyadd(dbProc[inx].dbproc, &m1, &m2, &mresult);
  2139. X
  2140. X            new_mnytochar(dbProc[inx].dbproc, &mresult, mnybuf);
  2141. X
  2142. X            --sp;  /* readjust to get rid of space preallocation */
  2143. X
  2144. X            (void)astore(ary,++sp,str_2mortal(str_nmake((double)retval)));
  2145. X        (void)astore(ary,++sp,str_2mortal(str_make(mnybuf, 0)));
  2146. X    }
  2147. X    break;
  2148. END_OF_FILE
  2149.   if test 48039 -ne `wc -c <'sybperl.c.A'`; then
  2150.     echo shar: \"'sybperl.c.A'\" unpacked with wrong size!
  2151.   elif test -f 'sybperl.c.B' ; then
  2152.     echo shar: Combining  \"'sybperl.c'\" \(84305 characters\)
  2153.     cat 'sybperl.c.A' 'sybperl.c.B' > 'sybperl.c'
  2154.     if test 84305 -ne `wc -c <'sybperl.c'`; then
  2155.       echo shar: \"'sybperl.c'\" combined with wrong size!
  2156.     else
  2157.       rm sybperl.c.A sybperl.c.B
  2158.     fi
  2159.   fi
  2160.   # end of 'sybperl.c.A'
  2161. fi
  2162. if test ! -d 't' ; then
  2163.     echo shar: Creating directory \"'t'\"
  2164.     mkdir 't'
  2165. fi
  2166. echo shar: End of archive 1 \(of 3\).
  2167. cp /dev/null ark1isdone
  2168. MISSING=""
  2169. for I in 1 2 3 ; do
  2170.     if test ! -f ark${I}isdone ; then
  2171.     MISSING="${MISSING} ${I}"
  2172.     fi
  2173. done
  2174. if test "${MISSING}" = "" ; then
  2175.     echo You have unpacked all 3 archives.
  2176.     rm -f ark[1-9]isdone
  2177. else
  2178.     echo You still must unpack the following archives:
  2179.     echo "        " ${MISSING}
  2180. fi
  2181. exit 0
  2182. exit 0 # Just in case...
  2183.