1package main
2
3import (
4	"fmt"
5	"os"
6
7	"github.com/prasmussen/gdrive/cli"
8)
9
10const Name = "gdrive"
11const Version = "2.1.1"
12
13const DefaultMaxFiles = 30
14const DefaultMaxChanges = 100
15const DefaultNameWidth = 40
16const DefaultPathWidth = 60
17const DefaultUploadChunkSize = 8 * 1024 * 1024
18const DefaultTimeout = 5 * 60
19const DefaultQuery = "trashed = false and 'me' in owners"
20const DefaultShareRole = "reader"
21const DefaultShareType = "anyone"
22
23var DefaultConfigDir = GetDefaultConfigDir()
24
25func main() {
26	globalFlags := []cli.Flag{
27		cli.StringFlag{
28			Name:         "configDir",
29			Patterns:     []string{"-c", "--config"},
30			Description:  fmt.Sprintf("Application path, default: %s", DefaultConfigDir),
31			DefaultValue: DefaultConfigDir,
32		},
33		cli.StringFlag{
34			Name:        "refreshToken",
35			Patterns:    []string{"--refresh-token"},
36			Description: "Oauth refresh token used to get access token (for advanced users)",
37		},
38		cli.StringFlag{
39			Name:        "accessToken",
40			Patterns:    []string{"--access-token"},
41			Description: "Oauth access token, only recommended for short-lived requests because of short lifetime (for advanced users)",
42		},
43		cli.StringFlag{
44			Name:        "serviceAccount",
45			Patterns:    []string{"--service-account"},
46			Description: "Oauth service account filename, used for server to server communication without user interaction (filename path is relative to config dir)",
47		},
48	}
49
50	handlers := []*cli.Handler{
51		&cli.Handler{
52			Pattern:     "[global] list [options]",
53			Description: "List files",
54			Callback:    listHandler,
55			FlagGroups: cli.FlagGroups{
56				cli.NewFlagGroup("global", globalFlags...),
57				cli.NewFlagGroup("options",
58					cli.IntFlag{
59						Name:         "maxFiles",
60						Patterns:     []string{"-m", "--max"},
61						Description:  fmt.Sprintf("Max files to list, default: %d", DefaultMaxFiles),
62						DefaultValue: DefaultMaxFiles,
63					},
64					cli.StringFlag{
65						Name:         "query",
66						Patterns:     []string{"-q", "--query"},
67						Description:  fmt.Sprintf(`Default query: "%s". See https://developers.google.com/drive/search-parameters`, DefaultQuery),
68						DefaultValue: DefaultQuery,
69					},
70					cli.StringFlag{
71						Name:        "sortOrder",
72						Patterns:    []string{"--order"},
73						Description: "Sort order. See https://godoc.org/google.golang.org/api/drive/v3#FilesListCall.OrderBy",
74					},
75					cli.IntFlag{
76						Name:         "nameWidth",
77						Patterns:     []string{"--name-width"},
78						Description:  fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
79						DefaultValue: DefaultNameWidth,
80					},
81					cli.BoolFlag{
82						Name:        "absPath",
83						Patterns:    []string{"--absolute"},
84						Description: "Show absolute path to file (will only show path from first parent)",
85						OmitValue:   true,
86					},
87					cli.BoolFlag{
88						Name:        "skipHeader",
89						Patterns:    []string{"--no-header"},
90						Description: "Dont print the header",
91						OmitValue:   true,
92					},
93					cli.BoolFlag{
94						Name:        "sizeInBytes",
95						Patterns:    []string{"--bytes"},
96						Description: "Size in bytes",
97						OmitValue:   true,
98					},
99				),
100			},
101		},
102		&cli.Handler{
103			Pattern:     "[global] download [options] <fileId>",
104			Description: "Download file or directory",
105			Callback:    downloadHandler,
106			FlagGroups: cli.FlagGroups{
107				cli.NewFlagGroup("global", globalFlags...),
108				cli.NewFlagGroup("options",
109					cli.BoolFlag{
110						Name:        "force",
111						Patterns:    []string{"-f", "--force"},
112						Description: "Overwrite existing file",
113						OmitValue:   true,
114					},
115					cli.BoolFlag{
116						Name:        "skip",
117						Patterns:    []string{"-s", "--skip"},
118						Description: "Skip existing files",
119						OmitValue:   true,
120					},
121					cli.BoolFlag{
122						Name:        "recursive",
123						Patterns:    []string{"-r", "--recursive"},
124						Description: "Download directory recursively, documents will be skipped",
125						OmitValue:   true,
126					},
127					cli.StringFlag{
128						Name:        "path",
129						Patterns:    []string{"--path"},
130						Description: "Download path",
131					},
132					cli.BoolFlag{
133						Name:        "delete",
134						Patterns:    []string{"--delete"},
135						Description: "Delete remote file when download is successful",
136						OmitValue:   true,
137					},
138					cli.BoolFlag{
139						Name:        "noProgress",
140						Patterns:    []string{"--no-progress"},
141						Description: "Hide progress",
142						OmitValue:   true,
143					},
144					cli.BoolFlag{
145						Name:        "stdout",
146						Patterns:    []string{"--stdout"},
147						Description: "Write file content to stdout",
148						OmitValue:   true,
149					},
150					cli.IntFlag{
151						Name:         "timeout",
152						Patterns:     []string{"--timeout"},
153						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
154						DefaultValue: DefaultTimeout,
155					},
156				),
157			},
158		},
159		&cli.Handler{
160			Pattern:     "[global] download query [options] <query>",
161			Description: "Download all files and directories matching query",
162			Callback:    downloadQueryHandler,
163			FlagGroups: cli.FlagGroups{
164				cli.NewFlagGroup("global", globalFlags...),
165				cli.NewFlagGroup("options",
166					cli.BoolFlag{
167						Name:        "force",
168						Patterns:    []string{"-f", "--force"},
169						Description: "Overwrite existing file",
170						OmitValue:   true,
171					},
172					cli.BoolFlag{
173						Name:        "skip",
174						Patterns:    []string{"-s", "--skip"},
175						Description: "Skip existing files",
176						OmitValue:   true,
177					},
178					cli.BoolFlag{
179						Name:        "recursive",
180						Patterns:    []string{"-r", "--recursive"},
181						Description: "Download directories recursively, documents will be skipped",
182						OmitValue:   true,
183					},
184					cli.StringFlag{
185						Name:        "path",
186						Patterns:    []string{"--path"},
187						Description: "Download path",
188					},
189					cli.BoolFlag{
190						Name:        "noProgress",
191						Patterns:    []string{"--no-progress"},
192						Description: "Hide progress",
193						OmitValue:   true,
194					},
195				),
196			},
197		},
198		&cli.Handler{
199			Pattern:     "[global] upload [options] <path>",
200			Description: "Upload file or directory",
201			Callback:    uploadHandler,
202			FlagGroups: cli.FlagGroups{
203				cli.NewFlagGroup("global", globalFlags...),
204				cli.NewFlagGroup("options",
205					cli.BoolFlag{
206						Name:        "recursive",
207						Patterns:    []string{"-r", "--recursive"},
208						Description: "Upload directory recursively",
209						OmitValue:   true,
210					},
211					cli.StringSliceFlag{
212						Name:        "parent",
213						Patterns:    []string{"-p", "--parent"},
214						Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
215					},
216					cli.StringFlag{
217						Name:        "name",
218						Patterns:    []string{"--name"},
219						Description: "Filename",
220					},
221					cli.StringFlag{
222						Name:        "description",
223						Patterns:    []string{"--description"},
224						Description: "File description",
225					},
226					cli.BoolFlag{
227						Name:        "noProgress",
228						Patterns:    []string{"--no-progress"},
229						Description: "Hide progress",
230						OmitValue:   true,
231					},
232					cli.StringFlag{
233						Name:        "mime",
234						Patterns:    []string{"--mime"},
235						Description: "Force mime type",
236					},
237					cli.BoolFlag{
238						Name:        "share",
239						Patterns:    []string{"--share"},
240						Description: "Share file",
241						OmitValue:   true,
242					},
243					cli.BoolFlag{
244						Name:        "delete",
245						Patterns:    []string{"--delete"},
246						Description: "Delete local file when upload is successful",
247						OmitValue:   true,
248					},
249					cli.IntFlag{
250						Name:         "timeout",
251						Patterns:     []string{"--timeout"},
252						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
253						DefaultValue: DefaultTimeout,
254					},
255					cli.IntFlag{
256						Name:         "chunksize",
257						Patterns:     []string{"--chunksize"},
258						Description:  fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
259						DefaultValue: DefaultUploadChunkSize,
260					},
261				),
262			},
263		},
264		&cli.Handler{
265			Pattern:     "[global] upload - [options] <name>",
266			Description: "Upload file from stdin",
267			Callback:    uploadStdinHandler,
268			FlagGroups: cli.FlagGroups{
269				cli.NewFlagGroup("global", globalFlags...),
270				cli.NewFlagGroup("options",
271					cli.StringSliceFlag{
272						Name:        "parent",
273						Patterns:    []string{"-p", "--parent"},
274						Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
275					},
276					cli.IntFlag{
277						Name:         "chunksize",
278						Patterns:     []string{"--chunksize"},
279						Description:  fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
280						DefaultValue: DefaultUploadChunkSize,
281					},
282					cli.StringFlag{
283						Name:        "description",
284						Patterns:    []string{"--description"},
285						Description: "File description",
286					},
287					cli.StringFlag{
288						Name:        "mime",
289						Patterns:    []string{"--mime"},
290						Description: "Force mime type",
291					},
292					cli.BoolFlag{
293						Name:        "share",
294						Patterns:    []string{"--share"},
295						Description: "Share file",
296						OmitValue:   true,
297					},
298					cli.IntFlag{
299						Name:         "timeout",
300						Patterns:     []string{"--timeout"},
301						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
302						DefaultValue: DefaultTimeout,
303					},
304					cli.BoolFlag{
305						Name:        "noProgress",
306						Patterns:    []string{"--no-progress"},
307						Description: "Hide progress",
308						OmitValue:   true,
309					},
310				),
311			},
312		},
313		&cli.Handler{
314			Pattern:     "[global] update [options] <fileId> <path>",
315			Description: "Update file, this creates a new revision of the file",
316			Callback:    updateHandler,
317			FlagGroups: cli.FlagGroups{
318				cli.NewFlagGroup("global", globalFlags...),
319				cli.NewFlagGroup("options",
320					cli.StringSliceFlag{
321						Name:        "parent",
322						Patterns:    []string{"-p", "--parent"},
323						Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
324					},
325					cli.StringFlag{
326						Name:        "name",
327						Patterns:    []string{"--name"},
328						Description: "Filename",
329					},
330					cli.StringFlag{
331						Name:        "description",
332						Patterns:    []string{"--description"},
333						Description: "File description",
334					},
335					cli.BoolFlag{
336						Name:        "noProgress",
337						Patterns:    []string{"--no-progress"},
338						Description: "Hide progress",
339						OmitValue:   true,
340					},
341					cli.StringFlag{
342						Name:        "mime",
343						Patterns:    []string{"--mime"},
344						Description: "Force mime type",
345					},
346					cli.IntFlag{
347						Name:         "timeout",
348						Patterns:     []string{"--timeout"},
349						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
350						DefaultValue: DefaultTimeout,
351					},
352					cli.IntFlag{
353						Name:         "chunksize",
354						Patterns:     []string{"--chunksize"},
355						Description:  fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
356						DefaultValue: DefaultUploadChunkSize,
357					},
358				),
359			},
360		},
361		&cli.Handler{
362			Pattern:     "[global] info [options] <fileId>",
363			Description: "Show file info",
364			Callback:    infoHandler,
365			FlagGroups: cli.FlagGroups{
366				cli.NewFlagGroup("global", globalFlags...),
367				cli.NewFlagGroup("options",
368					cli.BoolFlag{
369						Name:        "sizeInBytes",
370						Patterns:    []string{"--bytes"},
371						Description: "Show size in bytes",
372						OmitValue:   true,
373					},
374				),
375			},
376		},
377		&cli.Handler{
378			Pattern:     "[global] mkdir [options] <name>",
379			Description: "Create directory",
380			Callback:    mkdirHandler,
381			FlagGroups: cli.FlagGroups{
382				cli.NewFlagGroup("global", globalFlags...),
383				cli.NewFlagGroup("options",
384					cli.StringSliceFlag{
385						Name:        "parent",
386						Patterns:    []string{"-p", "--parent"},
387						Description: "Parent id of created directory, can be specified multiple times to give many parents",
388					},
389					cli.StringFlag{
390						Name:        "description",
391						Patterns:    []string{"--description"},
392						Description: "Directory description",
393					},
394				),
395			},
396		},
397		&cli.Handler{
398			Pattern:     "[global] share [options] <fileId>",
399			Description: "Share file or directory",
400			Callback:    shareHandler,
401			FlagGroups: cli.FlagGroups{
402				cli.NewFlagGroup("global", globalFlags...),
403				cli.NewFlagGroup("options",
404					cli.StringFlag{
405						Name:         "role",
406						Patterns:     []string{"--role"},
407						Description:  fmt.Sprintf("Share role: owner/writer/commenter/reader, default: %s", DefaultShareRole),
408						DefaultValue: DefaultShareRole,
409					},
410					cli.StringFlag{
411						Name:         "type",
412						Patterns:     []string{"--type"},
413						Description:  fmt.Sprintf("Share type: user/group/domain/anyone, default: %s", DefaultShareType),
414						DefaultValue: DefaultShareType,
415					},
416					cli.StringFlag{
417						Name:        "email",
418						Patterns:    []string{"--email"},
419						Description: "The email address of the user or group to share the file with. Requires 'user' or 'group' as type",
420					},
421					cli.StringFlag{
422						Name:        "domain",
423						Patterns:    []string{"--domain"},
424						Description: "The name of Google Apps domain. Requires 'domain' as type",
425					},
426					cli.BoolFlag{
427						Name:        "discoverable",
428						Patterns:    []string{"--discoverable"},
429						Description: "Make file discoverable by search engines",
430						OmitValue:   true,
431					},
432					cli.BoolFlag{
433						Name:        "revoke",
434						Patterns:    []string{"--revoke"},
435						Description: "Delete all sharing permissions (owner roles will be skipped)",
436						OmitValue:   true,
437					},
438				),
439			},
440		},
441		&cli.Handler{
442			Pattern:     "[global] share list <fileId>",
443			Description: "List files permissions",
444			Callback:    shareListHandler,
445			FlagGroups: cli.FlagGroups{
446				cli.NewFlagGroup("global", globalFlags...),
447			},
448		},
449		&cli.Handler{
450			Pattern:     "[global] share revoke <fileId> <permissionId>",
451			Description: "Revoke permission",
452			Callback:    shareRevokeHandler,
453			FlagGroups: cli.FlagGroups{
454				cli.NewFlagGroup("global", globalFlags...),
455			},
456		},
457		&cli.Handler{
458			Pattern:     "[global] delete [options] <fileId>",
459			Description: "Delete file or directory",
460			Callback:    deleteHandler,
461			FlagGroups: cli.FlagGroups{
462				cli.NewFlagGroup("global", globalFlags...),
463				cli.NewFlagGroup("options",
464					cli.BoolFlag{
465						Name:        "recursive",
466						Patterns:    []string{"-r", "--recursive"},
467						Description: "Delete directory and all it's content",
468						OmitValue:   true,
469					},
470				),
471			},
472		},
473		&cli.Handler{
474			Pattern:     "[global] sync list [options]",
475			Description: "List all syncable directories on drive",
476			Callback:    listSyncHandler,
477			FlagGroups: cli.FlagGroups{
478				cli.NewFlagGroup("global", globalFlags...),
479				cli.NewFlagGroup("options",
480					cli.BoolFlag{
481						Name:        "skipHeader",
482						Patterns:    []string{"--no-header"},
483						Description: "Dont print the header",
484						OmitValue:   true,
485					},
486				),
487			},
488		},
489		&cli.Handler{
490			Pattern:     "[global] sync content [options] <fileId>",
491			Description: "List content of syncable directory",
492			Callback:    listRecursiveSyncHandler,
493			FlagGroups: cli.FlagGroups{
494				cli.NewFlagGroup("global", globalFlags...),
495				cli.NewFlagGroup("options",
496					cli.StringFlag{
497						Name:        "sortOrder",
498						Patterns:    []string{"--order"},
499						Description: "Sort order. See https://godoc.org/google.golang.org/api/drive/v3#FilesListCall.OrderBy",
500					},
501					cli.IntFlag{
502						Name:         "pathWidth",
503						Patterns:     []string{"--path-width"},
504						Description:  fmt.Sprintf("Width of path column, default: %d, minimum: 9, use 0 for full width", DefaultPathWidth),
505						DefaultValue: DefaultPathWidth,
506					},
507					cli.BoolFlag{
508						Name:        "skipHeader",
509						Patterns:    []string{"--no-header"},
510						Description: "Dont print the header",
511						OmitValue:   true,
512					},
513					cli.BoolFlag{
514						Name:        "sizeInBytes",
515						Patterns:    []string{"--bytes"},
516						Description: "Size in bytes",
517						OmitValue:   true,
518					},
519				),
520			},
521		},
522		&cli.Handler{
523			Pattern:     "[global] sync download [options] <fileId> <path>",
524			Description: "Sync drive directory to local directory",
525			Callback:    downloadSyncHandler,
526			FlagGroups: cli.FlagGroups{
527				cli.NewFlagGroup("global", globalFlags...),
528				cli.NewFlagGroup("options",
529					cli.BoolFlag{
530						Name:        "keepRemote",
531						Patterns:    []string{"--keep-remote"},
532						Description: "Keep remote file when a conflict is encountered",
533						OmitValue:   true,
534					},
535					cli.BoolFlag{
536						Name:        "keepLocal",
537						Patterns:    []string{"--keep-local"},
538						Description: "Keep local file when a conflict is encountered",
539						OmitValue:   true,
540					},
541					cli.BoolFlag{
542						Name:        "keepLargest",
543						Patterns:    []string{"--keep-largest"},
544						Description: "Keep largest file when a conflict is encountered",
545						OmitValue:   true,
546					},
547					cli.BoolFlag{
548						Name:        "deleteExtraneous",
549						Patterns:    []string{"--delete-extraneous"},
550						Description: "Delete extraneous local files",
551						OmitValue:   true,
552					},
553					cli.BoolFlag{
554						Name:        "dryRun",
555						Patterns:    []string{"--dry-run"},
556						Description: "Show what would have been transferred",
557						OmitValue:   true,
558					},
559					cli.BoolFlag{
560						Name:        "noProgress",
561						Patterns:    []string{"--no-progress"},
562						Description: "Hide progress",
563						OmitValue:   true,
564					},
565					cli.IntFlag{
566						Name:         "timeout",
567						Patterns:     []string{"--timeout"},
568						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
569						DefaultValue: DefaultTimeout,
570					},
571				),
572			},
573		},
574		&cli.Handler{
575			Pattern:     "[global] sync upload [options] <path> <fileId>",
576			Description: "Sync local directory to drive",
577			Callback:    uploadSyncHandler,
578			FlagGroups: cli.FlagGroups{
579				cli.NewFlagGroup("global", globalFlags...),
580				cli.NewFlagGroup("options",
581					cli.BoolFlag{
582						Name:        "keepRemote",
583						Patterns:    []string{"--keep-remote"},
584						Description: "Keep remote file when a conflict is encountered",
585						OmitValue:   true,
586					},
587					cli.BoolFlag{
588						Name:        "keepLocal",
589						Patterns:    []string{"--keep-local"},
590						Description: "Keep local file when a conflict is encountered",
591						OmitValue:   true,
592					},
593					cli.BoolFlag{
594						Name:        "keepLargest",
595						Patterns:    []string{"--keep-largest"},
596						Description: "Keep largest file when a conflict is encountered",
597						OmitValue:   true,
598					},
599					cli.BoolFlag{
600						Name:        "deleteExtraneous",
601						Patterns:    []string{"--delete-extraneous"},
602						Description: "Delete extraneous remote files",
603						OmitValue:   true,
604					},
605					cli.BoolFlag{
606						Name:        "dryRun",
607						Patterns:    []string{"--dry-run"},
608						Description: "Show what would have been transferred",
609						OmitValue:   true,
610					},
611					cli.BoolFlag{
612						Name:        "noProgress",
613						Patterns:    []string{"--no-progress"},
614						Description: "Hide progress",
615						OmitValue:   true,
616					},
617					cli.IntFlag{
618						Name:         "timeout",
619						Patterns:     []string{"--timeout"},
620						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
621						DefaultValue: DefaultTimeout,
622					},
623					cli.IntFlag{
624						Name:         "chunksize",
625						Patterns:     []string{"--chunksize"},
626						Description:  fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
627						DefaultValue: DefaultUploadChunkSize,
628					},
629				),
630			},
631		},
632		&cli.Handler{
633			Pattern:     "[global] changes [options]",
634			Description: "List file changes",
635			Callback:    listChangesHandler,
636			FlagGroups: cli.FlagGroups{
637				cli.NewFlagGroup("global", globalFlags...),
638				cli.NewFlagGroup("options",
639					cli.IntFlag{
640						Name:         "maxChanges",
641						Patterns:     []string{"-m", "--max"},
642						Description:  fmt.Sprintf("Max changes to list, default: %d", DefaultMaxChanges),
643						DefaultValue: DefaultMaxChanges,
644					},
645					cli.StringFlag{
646						Name:         "pageToken",
647						Patterns:     []string{"--since"},
648						Description:  fmt.Sprintf("Page token to start listing changes from"),
649						DefaultValue: "1",
650					},
651					cli.BoolFlag{
652						Name:        "now",
653						Patterns:    []string{"--now"},
654						Description: fmt.Sprintf("Get latest page token"),
655						OmitValue:   true,
656					},
657					cli.IntFlag{
658						Name:         "nameWidth",
659						Patterns:     []string{"--name-width"},
660						Description:  fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
661						DefaultValue: DefaultNameWidth,
662					},
663					cli.BoolFlag{
664						Name:        "skipHeader",
665						Patterns:    []string{"--no-header"},
666						Description: "Dont print the header",
667						OmitValue:   true,
668					},
669				),
670			},
671		},
672		&cli.Handler{
673			Pattern:     "[global] revision list [options] <fileId>",
674			Description: "List file revisions",
675			Callback:    listRevisionsHandler,
676			FlagGroups: cli.FlagGroups{
677				cli.NewFlagGroup("global", globalFlags...),
678				cli.NewFlagGroup("options",
679					cli.IntFlag{
680						Name:         "nameWidth",
681						Patterns:     []string{"--name-width"},
682						Description:  fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
683						DefaultValue: DefaultNameWidth,
684					},
685					cli.BoolFlag{
686						Name:        "skipHeader",
687						Patterns:    []string{"--no-header"},
688						Description: "Dont print the header",
689						OmitValue:   true,
690					},
691					cli.BoolFlag{
692						Name:        "sizeInBytes",
693						Patterns:    []string{"--bytes"},
694						Description: "Size in bytes",
695						OmitValue:   true,
696					},
697				),
698			},
699		},
700		&cli.Handler{
701			Pattern:     "[global] revision download [options] <fileId> <revId>",
702			Description: "Download revision",
703			Callback:    downloadRevisionHandler,
704			FlagGroups: cli.FlagGroups{
705				cli.NewFlagGroup("global", globalFlags...),
706				cli.NewFlagGroup("options",
707					cli.BoolFlag{
708						Name:        "force",
709						Patterns:    []string{"-f", "--force"},
710						Description: "Overwrite existing file",
711						OmitValue:   true,
712					},
713					cli.BoolFlag{
714						Name:        "noProgress",
715						Patterns:    []string{"--no-progress"},
716						Description: "Hide progress",
717						OmitValue:   true,
718					},
719					cli.BoolFlag{
720						Name:        "stdout",
721						Patterns:    []string{"--stdout"},
722						Description: "Write file content to stdout",
723						OmitValue:   true,
724					},
725					cli.StringFlag{
726						Name:        "path",
727						Patterns:    []string{"--path"},
728						Description: "Download path",
729					},
730					cli.IntFlag{
731						Name:         "timeout",
732						Patterns:     []string{"--timeout"},
733						Description:  fmt.Sprintf("Set timeout in seconds, use 0 for no timeout. Timeout is reached when no data is transferred in set amount of seconds, default: %d", DefaultTimeout),
734						DefaultValue: DefaultTimeout,
735					},
736				),
737			},
738		},
739		&cli.Handler{
740			Pattern:     "[global] revision delete <fileId> <revId>",
741			Description: "Delete file revision",
742			Callback:    deleteRevisionHandler,
743			FlagGroups: cli.FlagGroups{
744				cli.NewFlagGroup("global", globalFlags...),
745			},
746		},
747		&cli.Handler{
748			Pattern:     "[global] import [options] <path>",
749			Description: "Upload and convert file to a google document, see 'about import' for available conversions",
750			Callback:    importHandler,
751			FlagGroups: cli.FlagGroups{
752				cli.NewFlagGroup("global", globalFlags...),
753				cli.NewFlagGroup("options",
754					cli.StringSliceFlag{
755						Name:        "parent",
756						Patterns:    []string{"-p", "--parent"},
757						Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
758					},
759					cli.BoolFlag{
760						Name:        "noProgress",
761						Patterns:    []string{"--no-progress"},
762						Description: "Hide progress",
763						OmitValue:   true,
764					},
765					cli.StringFlag{
766						Name:        "mime",
767						Patterns:    []string{"--mime"},
768						Description: "Mime type of imported file",
769					},
770				),
771			},
772		},
773		&cli.Handler{
774			Pattern:     "[global] export [options] <fileId>",
775			Description: "Export a google document",
776			Callback:    exportHandler,
777			FlagGroups: cli.FlagGroups{
778				cli.NewFlagGroup("global", globalFlags...),
779				cli.NewFlagGroup("options",
780					cli.BoolFlag{
781						Name:        "force",
782						Patterns:    []string{"-f", "--force"},
783						Description: "Overwrite existing file",
784						OmitValue:   true,
785					},
786					cli.StringFlag{
787						Name:        "mime",
788						Patterns:    []string{"--mime"},
789						Description: "Mime type of exported file",
790					},
791					cli.BoolFlag{
792						Name:        "printMimes",
793						Patterns:    []string{"--print-mimes"},
794						Description: "Print available mime types for given file",
795						OmitValue:   true,
796					},
797				),
798			},
799		},
800		&cli.Handler{
801			Pattern:     "[global] about [options]",
802			Description: "Google drive metadata, quota usage",
803			Callback:    aboutHandler,
804			FlagGroups: cli.FlagGroups{
805				cli.NewFlagGroup("global", globalFlags...),
806				cli.NewFlagGroup("options",
807					cli.BoolFlag{
808						Name:        "sizeInBytes",
809						Patterns:    []string{"--bytes"},
810						Description: "Show size in bytes",
811						OmitValue:   true,
812					},
813				),
814			},
815		},
816		&cli.Handler{
817			Pattern:     "[global] about import",
818			Description: "Show supported import formats",
819			Callback:    aboutImportHandler,
820			FlagGroups: cli.FlagGroups{
821				cli.NewFlagGroup("global", globalFlags...),
822			},
823		},
824		&cli.Handler{
825			Pattern:     "[global] about export",
826			Description: "Show supported export formats",
827			Callback:    aboutExportHandler,
828			FlagGroups: cli.FlagGroups{
829				cli.NewFlagGroup("global", globalFlags...),
830			},
831		},
832		&cli.Handler{
833			Pattern:     "version",
834			Description: "Print application version",
835			Callback:    printVersion,
836		},
837		&cli.Handler{
838			Pattern:     "help",
839			Description: "Print help",
840			Callback:    printHelp,
841		},
842		&cli.Handler{
843			Pattern:     "help <command>",
844			Description: "Print command help",
845			Callback:    printCommandHelp,
846		},
847		&cli.Handler{
848			Pattern:     "help <command> <subcommand>",
849			Description: "Print subcommand help",
850			Callback:    printSubCommandHelp,
851		},
852	}
853
854	cli.SetHandlers(handlers)
855
856	if ok := cli.Handle(os.Args[1:]); !ok {
857		ExitF("No valid arguments given, use '%s help' to see available commands", Name)
858	}
859}
860