Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# Notes
- --# Notes
- --[[
- This utility backs up and restores your projects for you, by storing them as images in your Dropbox folder.
- Its features are as follows:
- 1. backs up up behind the scenes every time you change the version number
- 2. tells you how long it is since the last backup
- 3. restores your backup into a new project, tabs and all
- INSTALLING IT
- Copy this project to your Codea, named as Backup
- BACKING UP
- Include a line like this in your function setup()
- b=Backup("MyProject Ver 1.00")
- Also create a dependency to the Backup project
- (click the + at upper right of screen, find Backup on the project list, and press it)
- The utility will only back up when the string in brackets changes. It will create a new image with that name
- in your Dropbox folder (you may need to sync to see it). The reason for not backing up every time you run, is that when you are developing, you will try a lot of stuff, and you may get into a mess. You will then want to go back to your last stable version. So the idea is that whenever you are at a checkpoint, make a new version name.
- NB each time you run, the utility will tell you how long it is since you last backed up. You can turn this off by adding a second parameter, false, when calling Backup.
- RESTORING
- Create a new project to hold your restored project, and put this in function setup()
- img=readImage("Dropbox:AAA")
- r=Restore(img)
- Now change the image name to the Dropbox file you want to restore
- BEFORE YOU RUN, create a dependency to the Backup project
- (click the + at upper right of screen, find Backup on the project list, and press it)
- Now run, and when it's done, go back to the code and it should all be there.
- Ignatz version 1.10 March 2013
- --]]
- --# Main
- function setup()
- Tests()
- end
- --[[
- Backup file format - version 1
- 3 bytes version number (currently = 1)
- 7 bytes size of file
- then for each tab...
- 3 bytes char #!#
- tab name
- 3 bytes char #!#
- tab text
- there is no need for a closing marker because we know the file size
- NOTES
- The version number and size of file are stored digit by digit, so 125 takes 3 chars. It is not easily
- feasible to use ASCII encoding to make this more compact (ie 125 translates to one char of ASCII 125),
- because Codea only accepts about 80 valid characters. And we can afford a handful of extra bytes.
- The tab delimiter similarly has to come from the 80 odd valid characters, and to make it unique, needs to be a character combination never found in normal program text.
- --]]
- --# Test
- function Tests()
- success=true
- success=Test_Headers() --tests that the file headers work
- if success then success=Test_Split("rfv") end --tests that a pile of concatenated tab text can be split accurately
- if success then success=Test_Backup("rfv") end --tests the hol system using the project name provided
- end
- function Test_Headers()
- print("Testing file headers")
- result=".... passed"
- for i=1,100 do --do some tests
- --random string with specific prefix/suffix where problems are most likely to occur
- txt1="12345"..RandomText(20,1500).."67890"
- txt2=Backup:AddHeader(1,txt1)
- txt3=txt2..RandomText(0,100) --add chars to represent unused part of last col of image
- version,txt4=Restore:StripHeader(txt3)
- if version~="001" then result="Incorrect version" break end
- --if n~=string.len(txt1) then result="Incorrect length" break end
- if txt4~=txt1 then result="Incorrect text" break end
- end
- print(result)
- if result~=".... passed" then
- print("==== Original text ====")
- print(txt1)
- print("Size=",string.len(txt1))
- print("==== Text with header ====")
- print(txt2)
- print("==== Text with image padding ====")
- print(txt3)
- print("==== Decoded header details ====")
- print("Version",version)
- print("==== Decoded text ====")
- print(txt4)
- return false
- else return true
- end
- end
- function RandomText(n1,n2,c)
- --create a random string of printable chars
- --if you provide c, it will be used instead (sometimes it's easier to look at a repeated char than randoms)
- Codes="!#&~*+,)/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ]_`abcdefghijklmnopqrstuvwxyz"
- local t=""
- m=math.random(n1,n2)
- if c==nil then
- for i=1,m do
- x=math.random(1,string.len(Codes))
- t=t..string.sub(Codes,x,x)
- end
- else
- t=string.rep(c,m)
- end
- return t
- end
- function Test_Split(p)
- print("Testing code splitter")
- delim="#!#"
- tabs={}
- t={}
- tabs=listProjectTabs(p)
- n=#tabs
- txt=""
- for i=1,n do
- t[i]=readProjectTab(p..":"..tabs[i])
- txt=txt..delim..tabs[i]..delim..t[i]
- end
- result=".... passed"
- if n==0 then result="No tabs found" end
- for i=1,n do
- tabName,tabText,txt2=Restore:SplitCode(txt,delim,2)
- if tabName~=tabs[i] then result="Tab name incorrect" j=i break end
- if tabText~=t[i] or tabText=="" or tabText==nil then result="Text incorrect" j=i break end
- txt=txt2
- end
- print(result)
- if result~=".... passed" then
- print("Error in tab",j)
- print("==== Text passed ====")
- print(txt)
- print("==== Remaining text ====")
- print(txt2)
- print("==== Tab name ====")
- print("Correct name",tabs[j])
- print("Recovered name",tabName)
- print("==== Tab text ====")
- print("==Correct text==")
- print(t[j])
- print("==Recovered text==")
- print(tabText)
- return false
- else return true
- end
- end
- function Test_Backup(p)
- print("Testing complete system")
- b=Backup("Test "..tostring(math.random(1,1000)),true,p)
- end
- --# Backup
- Backup = class()
- --title is name of backup, backup only occurs when this changes
- --remind parameter if set to false turns off the reminder of how long it has been since last backup
- function Backup:init(title,remind,testproject)
- local t=readProjectData(title)
- if t~=nil and remind~=false then
- print("Last backup was",string.format("%.0f",os.difftime(os.time(),t)/60),"minutes ago")
- else
- local version="001"
- local delim="#!#" --tab and tabname delimiter
- self:Store(title,version,delim,testproject)
- saveProjectData(title,os.time())
- end
- end
- function Backup:Store(title,version,delim,testproject)
- local origTxt=""
- local tabs
- if testproject==nil then tabs=listProjectTabs() else tabs=listProjectTabs(testproject) end
- for i,t in pairs(tabs) do
- local tt=t
- if testproject~=nil then tt=testproject..":"..t end
- origTxt=origTxt..delim..t..delim..readProjectTab(tt)
- end
- local txt=Backup:AddHeader(version,origTxt) --add file header
- --setup image
- local n=string.len(txt)
- local s=math.floor((n/3)^.5)+1
- local img=image(s,s)
- local row,col=0,1
- local e={}
- local m=0
- for i=0,n-1,3 do
- for j=1,3 do
- e[j]=string.byte(string.sub(txt,i+j,i+j)) or 0
- end
- row = row + 1
- if row>s then row=1 col = col + 1 end
- img:set(col,row,e[1],e[2],e[3],255)
- end
- local docName="Dropbox:"..title
- saveImage(docName,nil)
- saveImage(docName,img)
- print(title.." - backup made")
- --verify image by restoring and comparing result with original
- local r=Restore(img,1)
- if origTxt==r.txt then
- print("Backup verified")
- else
- print("ERROR - backup faulty")
- print(r.txt)
- end
- end
- --parameters are version number and text
- function Backup:AddHeader(v,t)
- local n=tostring(string.len(t) + 10) -- 3 version bytes + 7 length bytes
- return Backup:Pad(tostring(v),3)..Backup:Pad(n,7)..t
- end
- function Backup:Pad(str,n)
- local m=string.len(str)
- if m>=n then
- return str
- else
- return string.rep("0",n-m)..str
- end
- end
- --# Restore
- Restore = class()
- function Restore:init(img,test)
- local txt=self:ReadImage(img)
- if txt=="ERROR" then
- print("ERROR: I couldn't read the backup")
- else
- local version,t=Restore:StripHeader(txt)
- if test==1 then self.txt=t return end
- if version=="001" then
- local delim="#!#" --tab and tabname delimiter
- Restore:SplitCode(t,delim,test)
- end
- end
- end
- function Restore:ReadImage(img)
- if type(img)=="string" then img=readImage(img) end
- rows,cols=img.height,img.width
- t={}
- local n=0
- local r,g,b
- for col=1,cols do
- for row=1,rows do
- r,g,b=img:get(col,row)
- table.insert(t,string.char(r)) table.insert(t,string.char(g)) table.insert(t,string.char(b))
- n = n + 3
- end
- end
- return table.concat(t)
- end
- function Restore:SplitCode(str,delim,test)
- local n=string.len(delim)
- local i,j=string.find(str,delim,nil,true)
- while i~=nil and j~=nil do
- local k=string.find(str,delim,j+1,true)
- local tabName=string.sub(str,j+1,k-1)
- i,j=string.find(str,delim,k+n,true)
- if i~=nil then
- tabText=string.sub(str,k+n,i-1)
- else
- tabText=string.sub(str,k+n)
- i=string.len(str)+1
- end
- if test==2 then
- return tabName,tabText,string.sub(str,i)
- elseif test==nil then
- print("Restoring",tabName)
- saveProjectTab(tabName,tabText)
- end
- end
- if test==nil then print("All done!") end
- end
- function Restore:StripHeader(txt)
- local version=string.sub(txt,1,3)
- local n=tonumber(string.sub(txt,4,10))
- local t=string.sub(txt,11,n)
- return version,t
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement