Wednesday, June 19, 2013

#1 2013-03-06 04:02:33 am

McUsrII
Member
Registered: 2012-11-20
Posts: 1155
Website

A library of "Database Routines" for Apple Script

Hello.

This is a collection of handlers, as found in the now so venerable Scripting Additions Guide, while you hopefully still can get it. I just dump this here, for the hope that anybody can use it.

The stuff is copyright Apple I guess, but they never copyrighted it, I take the full responsibilty for any faults I may have introduced, and there are no warranties, but I hope someone may find them useful, while they read the Scripting Additions Guide.

It is rather unpolished, but it did work for me.

Applescript:

--Listing 2-1    Reading a specific record from a text-based database file
--first choose data file to work with
set pathToUse to choose file
try
   set x to open for access pathToUse
   set z to ReadRecord(16, 1, tab, "
"
, x)
   close access x
   z --display requested record
on error errString number errNum
   -- 72 Using Read/Write CommandsCHAPTER 2
   -- Scripting Addition Commands
   display dialog errString
   close access x
end try
on ReadRecord(numberOfFields, whichRecord, fieldDelimiter, recordDelimiter, fileRefNum)
   try
       (*
if there’s a record delimiter, read all fields except for last using field delimiter, then
read last field using record delimiter *)

       if recordDelimiter is "" then
           set readxTimes to numberOfFields
       else
           set readxTimes to numberOfFields - 1
       end if
       repeat whichRecord times
           set recordData to {}
           repeat (readxTimes) times
               set recordData to recordData & {(read fileRefNum before fieldDelimiter)}
           end repeat
           if readxTimes is not numberOfFields then
               set recordData to recordData & {(read fileRefNum before recordDelimiter)}
           end if
       end repeat
       return recordData
   on error errString number errNum
       display dialog errString
       return errString
   end try
end ReadRecord

Applescript:

-- Listing 2-2    Deleting a record from a text-based database file
--choose data file to use

-- global startSize, idx, preRecordSize, accumulatedSize, readxTimes, fileBuffer, q
-- set q to 0
-- set startSize to 0
-- set preRecordSize to 0
-- set accumulatedSize to 0
-- set readxTimes to 0
-- Debugget MYE 3/6/2012 Tommy Bollman
-- A) sørget for at vi beholdt return på linja vi har lest inn.
-- B) Sørget for at den nye fil størrelse ble korrekt, med hensyn til beregning av EOF
-- C) Sørget for at vi ikke utfører ulovlige operasjoner ved purge. enten hele fra start, eller siste post.

set pathToUse to choose file
--74    Using Read/Write CommandsCHAPTER 2
-- Scripting Addition Commands
try
   set x to open for access pathToUse with write permission
   DeleteRecord(16, 1, ";", return, x)
   
   close access x
on error errString number errNum
   display dialog errString
   close access x
end try


on DeleteRecord(numberOfFields, whichRecord, fieldDelimiter, recordDelimiter, fileRefNum)
   try
       --initialize variables
       set startSize to get eof fileRefNum --current size
       set idx to 1 --counter
       set preRecordSize to 1 --offset of record to delete
       set accumulatedSize to 0 --total size of records read
       if recordDelimiter is "" then
           set readxTimes to numberOfFields
       else
           set readxTimes to numberOfFields - 1
       end if
       repeat with idx from 1 to whichRecord
           repeat (readxTimes) times
               set q to read fileRefNum as text until fieldDelimiter
               set accumulatedSize to accumulatedSize + (length of q)
           end repeat
           if readxTimes is not numberOfFields then
               set q to read fileRefNum as text until recordDelimiter
               set accumulatedSize to accumulatedSize + (length of q)
           end if
           
           -- Using Read/Write Commands    75
           -- CHAPTER 2
           --Scripting Addition Commands
           (*
if record to delete is the first record in file or the next record that will be read,
set preRecordSize *)

           if whichRecord is 1 or idx is whichRecord - 1 then
               if whichRecord is 1 then
                   set preRecordSize to 0 -- var 1 BUG A korreksjon
               else
                   set preRecordSize to accumulatedSize
               end if
           end if
       end repeat
       (* now that preRecordSize is determined, read the record to be deleted so file mark is
set to beginning of next record *)

       
       
       if (startSize - accumulatedSize) is not 0 then
           set fileBuffer to read fileRefNum from accumulatedSize + 1 -- BUG C korreksjon
           --next, overwrite record to be deleted with remainder of file
           write fileBuffer to fileRefNum as text starting at preRecordSize + 1 -- BUG A korreksjon
           set eof fileRefNum to (startSize - (accumulatedSize - preRecordSize)) -- BUG B korreksjon
       else
           (*
if the file contains only the record to be deleted,
set the end of the file to 0 *)

           if whichRecord is 1 then
               set eof fileRefNum to 0 (*
if record to be deleted is last record in file, just shrink the file *)

           else
               set eof fileRefNum to preRecordSize -- BUG A korreksjon INGEN; recordskiller er med her
           end if
       end if
   on error errString number errNum
       display dialog errString
   end try
end DeleteRecord

Applescript:


-- Listing 2-3    Inserting a record in a database file
--choose file to work with
set pathToUse to choose file
-- Using Read/Write Commands    77CHAPTER 2
-- Scripting Addition Commands
try
   (* first put the record to be added into a variable; in this case the record to be added is actually an AppleScript list because the file on disk doesn’t include label data *)
   set newRecord to {"Granny", "Knutsen", "123 Potato Chip Lane", "Palo Minnow", "CA", "98761", "Snackable Computer", "888-987-0987", "978 -234-5432", "123-985-1122"}
   set x to open for access pathToUse with write permission
   AddRecord(newRecord, -1, tab, return, x) -- addrecord # 5 uheldig når det ikke er 5 records i fil...
   close access x
on error errString number errNum
   display dialog errString
   close access x
end try

on AddRecord(recordToAdd, addWhere, fieldDelimiter, recordDelimiter, fileRefNum)
   try
       --initialize variables
       set idx to 1 --counter
       set preRecordSize to 1 --offset of byte at which to add file
       set accumulatedSize to 0 --total size of records read
       set numberOfFields to count of recordToAdd
       if recordDelimiter is "" then
           set readxTimes to numberOfFields
       else
           set readxTimes to numberOfFields - 1
       end if
       
       (* Addendum 9/6 2012 17:18 writing a record to the end of file, this can happen
           when creating a file, or the item will be inserted at the end! *)

       
       if addWhere is -1 then
           set fileend to get eof of fileRefNum
           write "" to fileRefNum starting at fileend + 1 --sets write pointer to point at eof.
           WriteNewRecord(recordToAdd, fieldDelimiter, recordDelimiter, fileRefNum) --now add back the rest of the record
           return
       end if
       
       
       (*
if the record is to be added at the beginning of the file, this
If statement adds the record *)

       if addWhere is 1 then
           --read from beginning of file and store in postBuffer
           --offset of byte at which to add file 0 --total size of records read
           -- 78    Using Read/Write Commands
           --CHAPTER 2
           -- Scripting Addition Commands
           --        try
           -- for det tilfelle at fila var tom!
           set postBuffer to read fileRefNum from 1
           --    on error
           -- set postBuffer to ""
           --    end try
           (* before writing new record, file mark must be reset to beginning of file; to do this,
write an empty string to the beginning of file *)

           write "" to fileRefNum starting at 0
           WriteNewRecord(recordToAdd, fieldDelimiter, recordDelimiter, fileRefNum) --now add back the rest of the record
           write postBuffer to fileRefNum
           return
       end if
       
       (*
if the record is to be added somewhere other than at the beginning of the file, the rest of the AddRecord handler is executed *)

       repeat with idx from 1 to addWhere - 1
           repeat (readxTimes) times
               set q to read fileRefNum until fieldDelimiter
               set accumulatedSize to accumulatedSize + (length of q)
           end repeat
           if readxTimes is not numberOfFields then
               set q to read fileRefNum until recordDelimiter
               set accumulatedSize to accumulatedSize + (length of q)
           end if
       end repeat
       (* read from beginning of file to the byte at which the new record is to be added *)
       set postBuffer to read fileRefNum from accumulatedSize + 1
       (* before writing new record,
set file mark to byte at which new record is to be added; to do this,
write an empty string to that byte *)

       write "" to fileRefNum starting at accumulatedSize + 1
       WriteNewRecord(recordToAdd, fieldDelimiter, recordDelimiter, fileRefNum)
       -- Using Read/Write Commands    79
       -- CHAPTER 2
       --Scripting Addition Commands
       --now add back the rest of the record
       write postBuffer to fileRefNum
   on error errString number errNum
       display dialog errString
   end try
end AddRecord



on WriteNewRecord(recordToAdd, fieldDelimiter, recordDelimiter, fileRefNum)
   try
       set numberOfFields to count of recordToAdd
       if recordDelimiter is "" then
           set readxTimes to numberOfFields
       else
           set readxTimes to numberOfFields - 1
       end if
       repeat with idx from 1 to numberOfFields
           if idx ≤ readxTimes then
               write item idx of recordToAdd & fieldDelimiter to fileRefNum
           else
               (*
if file uses a record delimiter,
write delimiter after the last field in the record *)

               write item idx of recordToAdd & recordDelimiter to fileRefNum
           end if
       end repeat
   on error errString number errNum
       -- 80    Using Read/Write CommandsCHAPTER 2
       --Scripting Addition Commands
       display dialog errString
   end try
end WriteNewRecord

Applescript:

script textDatabase
   
   -- Start Clipping:
   -- clippingsname: OpenFileIfItExists({hfsPathAsText;theFile,blnIfWritingAllowed;writePermission,forceCreation;blnIfMakeFileSilently,procnameAsText;scriptName,appBundleId;frontappId}) -- Returns Errnumber, or File Reference Number
   -- clippingscontents: textDatabase's OpenFileIfItExists({hfsPathAsText:theFile,blnIfWritingAllowed:writePermission,forceCreation:blnIfMakeFileSilently,procnameAsText:scriptName,appBundleId:frontappId}) -- Returns Errnumber, or File Reference Number
   -- End Clipping:
   -- on OpenFileIfItExists({hfsPathAsText:theFile,blnIfWritingAllowed:writePermission,forceCreation:blnIfMakeFileSilently,procnameAsText:scriptName,appBundleId:frontappId}) -- Returns Errnumber, or File Reference Number
   on OpenFileIfItExists(R) -- Returns Errnumber, or File Reference Number
       -- R : {hfsPathAsText:theFile,blnIfWritingAllowed:writePermission,forceCreation:blnIfMakeFileSilently,procnameAsText:scriptName,appBundleId:frontappId}
       local AlertStopIconFile, x, theFirstErrMsg, theSecondErrMsg, firstErrorNum, secondErrorNum, thethirdErrMsg, thirdErrnumber, btnResult, failed, thefourthErrMsg, fourthErrnumber
       
       if not (forceCreation of R) then
           set AlertStopIconFile to a reference to file ((path to library folder from system domain as text) & "CoreServices:CoreTypes.bundle:Contents:Resources:AlertStopIcon.icns")
           -- set AlertStopIconFile to textDatabase's EnvironmentLIb's getResourceIconFile({icsFile:"AlertStopIcon.icns", appBundleId:(appBundleId of R)})
       end if
       set V to {emsg:"", num:0}
       try
           (*
if ( hfsPathAsText of R) doesn’t exist, Info For returns error -43 *)

           set x to info for file (hfsPathAsText of R)
           if (blnIfWritingAllowed of R) is true then
               set num of V to (open for access file (hfsPathAsText of R) with write permission)
           else
               --82    Using Read/Write CommandsCHAPTER 2
               --Scripting Addition Commands
               set num of V to (open for access file (hfsPathAsText of R))
           end if
       on error theFirstErrMsg number firstErrorNum
           if not (forceCreation of R) then
               
               try
                   --if error is -43, the user can choose to create the file
                   set btnResult to ""
                   tell application "SystemUIServer"
                       activate
                       set failed to false
                       try
                           set btnResult to button returned of (display dialog (my textDatabaseprivate's makePosixPath(hfsPathAsText of R) & "
Does Not Exist"
) with title (procnameAsText of R) buttons {"Create It For Me", "Cancel", "Ok"} default button 2 cancel button 2 with icon AlertStopIconFile)
                       on error theSecondErrMsg number secondErrorNum
                           if n is -128 then
                               set failed to true
                           else
                               set emsg of V to theSecondErrMsg
                               set num of V to secondErrorNum
                               return V
                           end if
                       end try
                   end tell
                   if failed is true then
                       set emsg of V to "User Aborted It"
                       set num of V to -60000
                       return V
                   end if
                   if btnResult is "Ok" then
                       set emsg of V to theFirstErrMsg
                       set num of V to firstErrorNum
                       return V
                   else
                       --create the file
                       if (blnIfWritingAllowed of R) is true then
                           set num of V to open for access file (hfsPathAsText of R) with write permission
                       else
                           set num of V to open for access file (hfsPathAsText of R)
                       end if
                   end if
               on error thethirdErrMsg number thirdErrnumber
                   set emsg of V to thethirdErrMsg
                   set num of V to thirdErrnumber
               end try
               return V
           else -- just force the creation
               try
                   if (blnIfWritingAllowed of R) is true then
                       
                       set num of V to open for access file (hfsPathAsText of R) with write permission
                   else
                       set num of V to open for access file (hfsPathAsText of R)
                   end if
               on error thefourthErrMsg number fourthErrnumber
                   set emsg of V to thefourthErrMsg
                   set num of V to fourthErrnumber
               end try
               return V
           end if
           
       end try
       return V
   end OpenFileIfItExists
   
   
   --    on OpenFileIfItExists(theFile, writePermission)
   --        set AlertStopIconFile to my EnvironmentLIb's getResourceIconFile({icsFile:"AlertStopIcon.icns", appBundleId:frontappId})
   --        try
   --            (*
   -- if theFile doesn’t exist, Info For returns error -43 *)
   --            set x to info for file theFile
   --            if writePermission is true then
   --                return (open for access file theFile with write permission)
   --            else
   --                --82    Using Read/Write CommandsCHAPTER 2
   --                --Scripting Addition Commands
   --                return (open for access file theFile)
   --            end if
   --        on error theErrMsg number errorNum
   --            try
   --                --if error is -43, the user can choose to create the file
   --                set btnResult to ""
   --                tell application "SystemUIServer"
   --                    activate
   --                    set failed to false
   --                    try
   --                        set btnResult to button returned of (display dialog theFile & "
   -- Does Not Exist" with title "Open File If It Exists" buttons {"Create It For Me", "Cancel", "Ok"} default button 2 cancel button 2 with icon file AlertStopIconFile)
   --                    on error e number n
   --                        set failed to true
   --                    end try
   --                end tell
   --                if failed is true then error number -6000
   --                if btnResult is "Ok" then
   --                    return errorNum
   --                else
   --                    --create the file
   --                    if writePermission is true then
   --                        return open for access file theFile with write permission
   --                    else
   --                        return open for access file theFile
   --                    end if
   --                end if
   --            on error theErrMsg number theerrnumber
   --                return theerrnumber
   --            end try
   --        end try
   --    end OpenFileIfItExists
   
   
   on writeheadOfFileReferenceAsUtf8BOM(fRef, stuff) -- Returns record, to store any error message in.
       -- ! NB! det siste tegnet i stuff bør være en newline...
       -- hakket mitt besørger at fil blir til ut f8 selv om det bare havner ascii i den
       -- etterpå!, så tegnsettet kommer korrekt ut i preview!
       
       local stuff, fRef, Msg, n, mybuffer, startsize, fsz, V
       set V to {emsg:"", num:0}
       try
           set startsize to get eof fRef
           if not startsize is 0 then
               set mybuffer to read fRef from 1 to startsize as «class utf8»
           else
               write «data rdatEFBBBF» to fRef -- as «class utf8» -- BOM Thanks! to Nigel Garvey
           end if
           
           write stuff to fRef as «class utf8» starting at 4
           if not startsize is 0 then
               write mybuffer to fRef as «class utf8»
           end if
           set fsz to get eof fRef
           close access fRef
           set num of V to fsz
       on error Msg number n
           try
               close access fRef
           end try
           set emsg of V to Msg
           set num of V to n
       end try
       return V
   end writeheadOfFileReferenceAsUtf8BOM
   
   -- mark jfileLib•writeTo<B
   -- >> jfileLib•writeTo
   (*
writeTo
writes given data to given file as given mode, and returns an alias to the file

Parameters:
f: file path, alias, posix path (the file to be written, can exist or not)
stuff: stuff to write to the file
mode: how to write the data (text, list, record, «class utf8», C string, etc)

Example:
writeTo("path:to:file.txt", {1,2,3}, list) --> alias "path:to:file.txt"
*)

   
   to writeToFileReferenceAsUtf8BOM(fRef, stuff, mode)
       set V to {emsg:"", num:0}
       
       local stuff, fRef, Msg, n, startsize, fsz, V
       
       try
           set eof of fRef to 0
           write «data rdatEFBBBF» to fRef -- as «class utf8» -- BOM Thanks! to Nigel Garvey
           write stuff to fRef as «class utf8» starting at 4
           set fsz to get eof fRef
           close access fRef
           set num of V to fsz
       on error Msg number n
           try
               close access fRef
           end try
           set emsg of V to Msg
           set num of V to n
       end try
       return V
   end writeToFileReferenceAsUtf8BOM
   
   
   on writeheadOfFileReferenceAsUtf8(fRef, stuff)
       -- ! NB! det siste tegnet i stuff bør være en newline...
       -- hakket mitt besørger at fil blir til ut f8 selv om det bare havner ascii i den
       -- etterpå! Virker i bbedit, ikke i preview!
       
       local stuff, fRef, Msg, n, mybuffer, startsize, fsz
       
       try
           set startsize to get eof fRef
           if not startsize is 0 then
               set mybuffer to read fRef from 1 to startsize as «class utf8»
           else
               write "æøå" to fRef starting at 0 as «class utf8» --utf8 no bom!
           end if
           set eof fRef to 0
           write stuff to fRef as «class utf8»
           if not startsize is 0 then
               write mybuffer to fRef as «class utf8»
           end if
           set fsz to get eof fRef
           close access fRef
           return fsz
       on error Msg number n
           try
               close access fRef
           end try
           return n
       end try
   end writeheadOfFileReferenceAsUtf8
   
   
   
   on writeTailOfFileReferenceAsUtf8(fRef, stuff)
       -- Denne brukes til begge utf8 variantene.
       local stuff, fRef, Msg, n, fsz
       try
           write stuff to fRef starting at eof as «class utf8»
           set fsz to get eof fRef
           close access fRef
           return fsz
       on error Msg number n
           try
               close access fRef
           end try
           return n
       end try
   end writeTailOfFileReferenceAsUtf8
   
   
   on writeheadOfFileReference(fRef, stuff)
       -- ! NB! det siste tegnet i stuff bør være en newline...
       -- Skriver ikke u-tf 8 automatisk. på min maskin...
       -- det avhenger vel av hva slags tegn som kommer inn der...
       -- mac roman kan ofte bli resultatet
       local stuff, fRef, Msg, n, mybuffer, startsize, fsz
       
       try
           set startsize to get eof fRef
           if not startsize is 0 then
               set mybuffer to read fRef from 1 to startsize
               set eof fRef to 0
           end if
           write stuff to fRef starting at 0
           if not startsize is 0 then
               write mybuffer to fRef
           end if
           set fsz to get eof fRef
           close access fRef
           return fsz
       on error Msg number n
           try
               close access fRef
           end try
           return n
       end try
   end writeheadOfFileReference
   
   
   on writeTailOfFileReference(fRef, stuff)
       local stuff, fRef, Msg, n, fsz
       try
           write stuff to fRef starting at eof
           set fsz to get eof fRef
           close access fRef
           return fsz
       on error Msg number n
           try
               close access fRef
           end try
           return n
           -- error msg number n
       end try
   end writeTailOfFileReference
   
   script textDatabaseprivate
       ----------------------------------------------------------------------------------
       -- unixpath method:
       --        Get the posix path (unix path).
       --        Doesn't handle accented characters. I'm open to suggestions that are not based on shell wildcards (too dangerous when deleting file(s)).
       ----------------------------------------------------------------------------------
       -- argument:
       --        macpath: an alias or path string
       -- return:
       --        posix path string
       ----------------------------------------------------------------------------------
       -- nicked from BBEdit lib
       on makePosixPath(macpath)
           set unixpath to POSIX path of macpath
           set unixpathlist to every character of unixpath
           repeat with i from 1 to length of unixpathlist
               -- took badchars list from http://www.macosxhints.com/article.php?story=20011217040113759
               set badchars to {" ", "'", "!", "$", "\"", "*", "(", ")", "{", "[", "|", ";", "<", ">", "?", "~", "`", "\\"}
               set thisItem to item i of unixpathlist as text
               if thisItem is in badchars then set item i of unixpathlist to ("\\" & thisItem)
           end repeat
           set unixpath to unixpathlist as string
           return unixpath
       end makePosixPath
   end script
   
end script


Filed under: applescript, Database

Offline

 

#2 2013-03-06 04:28:25 am

StefanK
Member
From: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 9826
Website

Re: A library of "Database Routines" for Apple Script

OS X has a built-in scriptable database application named Database Events (located in CoreServices folder)
which is quite unknown but easy to use


regards

Stefan

Offline

 

#3 2013-03-06 05:10:07 am

McUsrII
Member
Registered: 2012-11-20
Posts: 1155
Website

Re: A library of "Database Routines" for Apple Script

That is true, I have used it.

The code above makes for keeping a "database" in a CSV file, I should have mentioned that.

Actually at one point in time, I wondered about using the code above to export info from a database-events database into CSV. Which should be no problem really. smile


Filed under: Database

Offline

 

#4 2013-03-07 07:31:25 am

DJ Bazzie Wazzie
Member
From: the Netherlands
Registered: 2004-10-20
Posts: 1448
Website

Re: A library of "Database Routines" for Apple Script

McUsrII wrote:

The code above makes for keeping a "database" in a CSV file, I should have mentioned that.

An proper CSV uses string/text indicators (string fields are quoted) so it's not 100% CSV compatible. Therefore I've written an C command line utility that reads an CSV file and prints out AppleScript code than only need eval. In AppleScriptObjC I'm using Objective-C (with C code) and put it all in an NSArray (never coerce to appleScript list).


"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."
- Brian W. Kernighan

Offline

 

#5 2013-03-07 08:17:37 am

McUsrII
Member
Registered: 2012-11-20
Posts: 1155
Website

Re: A library of "Database Routines" for Apple Script

Please share it!

We both know that if we have the files, we can just "cat" the code into a directory, and type make from a terminal window! (I think the make that ships with developertools, knows where to find the frameworks by itself, the code won't be optimally compiled, but it should compile.) I volunteer! big_smile

I'll been abit lazy, I have the code to create "RFC-compliant" CSV, that is, you can specify a delimiter, the text are put between hyphens, and the delimiter is escaped within the string.

Maybe I should do that in Applescript? (I should add escapes for any hyphens too I guess.)

I'll do this a little by little.


Filed under: csv

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.088 seconds, 11 queries executed ]

RSS (new topics) RSS (active topics)