View difference between Paste ID: SrFAw1qj and 6pdzmUPp
SHOW: | | - or go back to the newest paste.
1
-- Gist Client for ComputerCraft
2
-- version 1.1
3
-- Matti Vapa, 2013
4
-- License: MIT
5
-- the client uses JSON4LUA module for parsing JSON (provided below)
6
-- client code starts around line 270
7
8
-- v1.1 noticed that you don't need to escape single quotes, per json specifications
9
-- v1.0 first version
10
11
12
-----------------------------------------------------------------------------
13
-- JSON4Lua: JSON encoding / decoding support for the Lua language.
14
-- json Module.
15
-- Author: Craig Mason-Jones
16
-- Homepage: http://json.luaforge.net/
17
-- Version: 0.9.40
18
-- This module is released under the MIT License (MIT).
19
20
-- edited for brevity
21
22
local base = _G
23
local decode_scanArray
24
local decode_scanComment
25
local decode_scanConstant
26
local decode_scanNumber
27
local decode_scanObject
28
local decode_scanString
29
local decode_scanWhitespace
30
local encodeString
31
local isArray
32
local isEncodable
33
34
local function encode (v)
35
  -- Handle nil values
36
  if v==nil then
37
    return "null"
38
  end
39
  
40
  local vtype = base.type(v)  
41
42
  -- Handle strings
43
  if vtype=='string' then    
44
    return '"' .. encodeString(v) .. '"'	    -- Need to handle encoding in string
45
  end
46
  
47
  -- Handle booleans
48
  if vtype=='number' or vtype=='boolean' then
49
    return base.tostring(v)
50
  end
51
  
52
  -- Handle tables
53
  if vtype=='table' then
54
    local rval = {}
55
    -- Consider arrays separately
56
    local bArray, maxCount = isArray(v)
57
    if bArray then
58
      for i = 1,maxCount do
59
        table.insert(rval, encode(v[i]))
60
      end
61
    else	-- An object, not an array
62
      for i,j in base.pairs(v) do
63
        if isEncodable(i) and isEncodable(j) then
64
          table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
65
        end
66
      end
67
    end
68
    if bArray then
69
      return '[' .. table.concat(rval,',') ..']'
70
    else
71
      return '{' .. table.concat(rval,',') .. '}'
72
    end
73
  end
74
  
75
  -- Handle null values
76
  if vtype=='function' and v==null then
77
    return 'null'
78
  end
79
  
80
  base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
81
end
82
83
local function decode(s, startPos)
84
  startPos = startPos and startPos or 1
85
  startPos = decode_scanWhitespace(s,startPos)
86
  base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
87
  local curChar = string.sub(s,startPos,startPos)
88
  -- Object
89
  if curChar=='{' then
90
    return decode_scanObject(s,startPos)
91
  end
92
  -- Array
93
  if curChar=='[' then
94
    return decode_scanArray(s,startPos)
95
  end
96
  -- Number
97
  if string.find("+-0123456789.e", curChar, 1, true) then
98
    return decode_scanNumber(s,startPos)
99
  end
100
  -- String
101
  if curChar==[["]] or curChar==[[']] then
102
    return decode_scanString(s,startPos)
103
  end
104
  if string.sub(s,startPos,startPos+1)=='/*' then
105
    return decode(s, decode_scanComment(s,startPos))
106
  end
107
  -- Otherwise, it must be a constant
108
  return decode_scanConstant(s,startPos)
109
end
110
111
local function null()
112
  return null -- so json.null() will also return null ;-)
113
end
114
115
116
function decode_scanArray(s,startPos)
117
  local array = {}	-- The return value
118
  local stringLen = string.len(s)
119
  base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
120
  startPos = startPos + 1
121
  -- Infinite loop for array elements
122
  repeat
123
    startPos = decode_scanWhitespace(s,startPos)
124
    base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
125
    local curChar = string.sub(s,startPos,startPos)
126
    if (curChar==']') then
127
      return array, startPos+1
128
    end
129
    if (curChar==',') then
130
      startPos = decode_scanWhitespace(s,startPos+1)
131
    end
132
    base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
133
    object, startPos = decode(s,startPos)
134
    table.insert(array,object)
135
  until false
136
end
137
138
function decode_scanComment(s, startPos)
139
  base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
140
  local endPos = string.find(s,'*/',startPos+2)
141
  base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
142
  return endPos+2  
143
end
144
145
function decode_scanConstant(s, startPos)
146
  local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
147
  local constNames = {"true","false","null"}
148
149
  for i,k in base.pairs(constNames) do
150
    --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
151
    if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
152
      return consts[k], startPos + string.len(k)
153
    end
154
  end
155
  base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
156
end
157
158
function decode_scanNumber(s,startPos)
159
  local endPos = startPos+1
160
  local stringLen = string.len(s)
161
  local acceptableChars = "+-0123456789.e"
162
  while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
163
	and endPos<=stringLen
164
	) do
165
    endPos = endPos + 1
166
  end
167
  local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
168
  local stringEval = base.loadstring(stringValue)
169
  base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
170
  return stringEval(), endPos
171
end
172
173
function decode_scanObject(s,startPos)
174
  local object = {}
175
  local stringLen = string.len(s)
176
  local key, value
177
  base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
178
  startPos = startPos + 1
179
  repeat
180
    startPos = decode_scanWhitespace(s,startPos)
181
    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
182
    local curChar = string.sub(s,startPos,startPos)
183
    if (curChar=='}') then
184
      return object,startPos+1
185
    end
186
    if (curChar==',') then
187
      startPos = decode_scanWhitespace(s,startPos+1)
188
    end
189
    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
190
    -- Scan the key
191
    key, startPos = decode(s,startPos)
192
    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
193
    startPos = decode_scanWhitespace(s,startPos)
194
    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
195
    base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
196
    startPos = decode_scanWhitespace(s,startPos+1)
197
    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
198
    value, startPos = decode(s,startPos)
199
    object[key]=value
200
  until false	-- infinite loop while key-value pairs are found
201
end
202
203
function decode_scanString(s,startPos)
204
  base.assert(startPos, 'decode_scanString(..) called without start position')
205
  local startChar = string.sub(s,startPos,startPos)
206
  base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
207
  local escaped = false
208
  local endPos = startPos + 1
209
  local bEnded = false
210
  local stringLen = string.len(s)
211
  repeat
212
    local curChar = string.sub(s,endPos,endPos)
213
    -- Character escaping is only used to escape the string delimiters
214
    if not escaped then	
215
      if curChar==[[\]] then
216
        escaped = true
217
      else
218
        bEnded = curChar==startChar
219
      end
220
    else
221
      -- If we're escaped, we accept the current character come what may
222
      escaped = false
223
    end
224
    endPos = endPos + 1
225
    base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
226
  until bEnded
227
  local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
228
  local stringEval = base.loadstring(stringValue)
229
  base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
230
  return stringEval(), endPos  
231
end
232
233
function decode_scanWhitespace(s,startPos)
234
  local whitespace=" \n\r\t"
235-
  s = string.gsub(s,"'",'\\"')
235+
236
  while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true)  and startPos <= stringLen) do
237
    startPos = startPos + 1
238
  end
239
  return startPos
240
end
241
242
function encodeString(s)
243
  s = string.gsub(s,'\\','\\\\')
244
  s = string.gsub(s,'"','\\"')
245
  s = string.gsub(s,'\n','\\n')
246
  s = string.gsub(s,'\t','\\t')
247
  return s 
248
end
249
250
function isArray(t)
251
  -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable 
252
  -- (with the possible exception of 'n')
253
  local maxIndex = 0
254
  for k,v in base.pairs(t) do
255
    if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then	-- k,v is an indexed pair
256
      if (not isEncodable(v)) then return false end	-- All array elements must be encodable
257
      maxIndex = math.max(maxIndex,k)
258
    else
259
      if (k=='n') then
260
        if v ~= table.getn(t) then return false end  -- False if n does not hold the number of elements
261
      else -- Else of (k=='n')
262
        if isEncodable(v) then return false end
263
      end  -- End of (k~='n')
264
    end -- End of k,v not an indexed pair
265
  end  -- End of loop across all pairs
266
  return true, maxIndex
267
end
268
269
function isEncodable(o)
270
  local t = base.type(o)
271
  return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) 
272
end
273
274
275
276
277
-- Gist Client
278
-- 2013 Matti Vapa
279
-- License: MIT
280
281
local url = "https://api.github.com/gists"
282
283
-- default parameters for POST
284
local putvars = {}
285
putvars["description"] = ""
286
putvars["public"] = true
287
putvars["files"] = {}
288
289
290
local printUsage = function()
291
	print("Usage:")
292
	print("gist get <id>")
293
	print("gist put <filename1> <filename2> ...")
294
end
295
296-
if #args == 2 and args[1] == "get"then
296+
297
local mode
298
299
if not http then
300
	print( "gist requires http API" )
301
	print( "Set enableAPI_http to 1 in mod_ComputerCraft.cfg" )
302
	return
303
end
304
305
if #args == 2 and args[1] == "get" then
306
	mode = "get"
307
elseif args[1] == "put" and #args >= 2 then
308
	mode = "put"
309
else
310
	printUsage()
311
	return
312
end
313
314
if mode == "get" then
315
	
316
	local id = args[2]
317
318
	local resp = http.get(url.."/"..id)
319
320
	if resp ~= nil then
321
		--print("Success with code "..tostring(resp.getResponseCode()).."!")
322
		local sresp = resp.readAll()
323
		resp.close()
324
		local data = decode(sresp)
325
		--iterate over the files (there can be several in one gist)
326
		for key, value in pairs(data["files"]) do
327
			local file = value["filename"]
328
			local localFile = file
329
			local path = shell.resolve(localFile)
330
			local confirm = true
331
			while fs.exists(path) do
332
				term.write("Local file "..localFile.." already exists. Overwrite? [y/n] ")
333
				local inp = io.read():lower()
334
				if inp ~= "y" then
335
					term.write("Download to a new local file? [y/n] ")
336
					local inp = io.read():lower()
337
					if inp ~= "y" then
338
						print("Skipping remote file: "..file)
339
						confirm = false
340
						break
341
					else
342
						term.write("Give a new file name: ")
343
						localFile = io.read()
344
						path = shell.resolve(localFile)
345
					end
346
				else
347
					print("Overwriting local file: "..localFile)
348
					break
349
				end
350
			end
351
			if confirm then
352
				local raw = http.get(value["raw_url"])
353
				if raw == nil then print("Unable to download contents of "..file.."!") return end
354
				local f = fs.open(path,"w")
355
				f.write(raw.readAll())
356
				f.close()
357
				raw.close()
358
				print("Remote file "..file.." downloaded!")
359
			end
360
		end
361
362
	else
363
		print("Failed to download gist with id "..id.."!")
364
		return
365
	end
366
367
elseif mode == "put" then
368
	local files = {}
369
	for i = 2,#args do
370
		local file = args[i]
371
		local path = shell.resolve(file)
372
		if not fs.exists(path) then
373
			print("No such file: "..file)
374
			return
375
		end
376
		local f = fs.open(path,"r")
377
		files[file] = {}
378
		files[file]["content"] = f.readAll()
379
		f.close()
380
	end
381
382
	putvars["files"] = files
383
384
	print("Give a description for the gist. (Can be blank)")
385
	putvars["description"] = io.read()
386
387
	term.write("Uploading to gist... ")
388
	local resp = http.post(url,encode(putvars))
389
390
	if resp ~= nil then
391
		print("Success!")
392
		--print("Success with code "..tostring(resp.getResponseCode()).."!")
393
		local data = decode(resp.readAll())
394
		resp.close()
395
		print("Gist id: "..tostring(data["id"])..".")
396
		print("Available for viewing at https://gist.github.com/"..tostring(data["id"]))
397
	else
398
		print("Failed.")
399
	end
400
end