gs

painless gist creator
git clone git://edryd.org/gs
Log | Files | Refs | LICENSE

commit 738b62f8b5178a8d9a60385e0842c677b8624294
parent 53586a2ec47dc79d2a0711aedb4fe96e80fc852b
Author: Ed van Bruggen <edvb@uw.edu>
Date:   Wed,  1 Apr 2020 21:00:29 -0700

Rename to gst to avoid name conflict

Diffstat:
.gitignore | 2+-
Makefile | 8++++----
README.md | 32++++++++++++++++----------------
config.mk | 2+-
gs.1 | 182-------------------------------------------------------------------------------
gs.c | 230-------------------------------------------------------------------------------
gst.1 | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gst.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 435 insertions(+), 434 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -5,7 +5,7 @@ \#*\# # compiled files -gs +gst *.exe *.out *.app diff --git a/Makefile b/Makefile @@ -1,10 +1,10 @@ -# gs - painless gist creator +# gst - painless gist creator # See LICENSE file for copyright and license details. include config.mk -EXE = gs -SRC = gs.c util.c extern/frozen.c +EXE = gst +SRC = gst.c util.c extern/frozen.c OBJ = $(SRC:.c=.o) all: options $(EXE) @@ -54,7 +54,7 @@ uninstall: man: $(EXE) @echo updating man page $(EXE).1 - @markman -nCD -t GS -V "$(EXE) $(VERSION)" -d "`date '+%B %Y'`" \ + @markman -nCD -t GST -V "$(EXE) $(VERSION)" -d "`date '+%B %Y'`" \ -s "`./$(EXE) -h 2>&1 | cut -d' ' -f2-`" README.md > $(EXE).1 .PHONY: all options clean install uninstall man diff --git a/README.md b/README.md @@ -1,7 +1,7 @@ -# gs \- painless gist creator +# gst \- painless gist creator Easy way to create GitHub Gists through the command line. Simply give it the -files to upload and gs will return the URL to the new gist. +files to upload and gst will return the URL to the new gist. ## Options @@ -55,21 +55,21 @@ Print version info and exit Create a new gist of `file.txt`: ``` -$ gs file.txt +$ gst file.txt https://gist.github.com/<new-id> ``` There is also support for multiple files in a single gist: ``` -$ gs README.md Makefile prog.c +$ gst README.md Makefile prog.c https://gist.github.com/<new-id> ``` Specify the gist's description: ``` -$ gs -d 'a cool shell script' script.sh +$ gst -d 'a cool shell script' script.sh https://gist.github.com/<new-id> ``` @@ -77,14 +77,14 @@ The gist's URL can be piped to other programs, for example to your clipboard to be pasted elsewhere: ``` -$ gs file.txt | xsel -bi +$ gst file.txt | xsel -bi ``` You can also create a new gist from `stdin`. The file name needs to be supplied however: ``` -$ cmd-which-errors | gs -f log.txt +$ cmd-which-errors | gst -f log.txt https://gist.github.com/<new-id> ``` @@ -92,7 +92,7 @@ The new gist can be posted under a GitHub user, a prompt will ask for your password: ``` -$ gs -u your-name good-proj.rs +$ gst -u your-name good-proj.rs GitHub password: https://gist.github.com/<new-id> ``` @@ -101,14 +101,14 @@ To skip the prompt your password can be supplied after a colon with the username: ``` -$ gs -u your-name:password plugin/func.vim doc/func.txt +$ gst -u your-name:password plugin/func.vim doc/func.txt https://gist.github.com/<new-id> ``` New gists can be private when created under a user: ``` -$ gs -p -u name personal.info +$ gst -p -u name personal.info https://gist.github.com/<new-id> ``` @@ -116,7 +116,7 @@ You can also edit a previously created gist under your account, such as adding a new file or updating the description: ``` -$ gs -e <id> -u name -d "new description" additional-file.txt +$ gst -e <id> -u name -d "new description" additional-file.txt https://gist.github.com/<id> ``` @@ -125,18 +125,18 @@ exists even if you delete all the files within it due to the limitations of the API: ``` -$ gs -e <id> -u name -D old.py +$ gst -e <id> -u name -D old.py https://gist.github.com/<id> ``` ## Installation -The only dependency needed to build gs is cURL. gs be compiled with a simple +The only dependency needed to build gst is cURL. gst be compiled with a simple `make` and `sudo make install` will install the executable and man page. ## Customization -gs can be customized by editing the custom `config.h` and (re)compiling the +gst can be customized by editing the custom `config.h` and (re)compiling the source code. This keeps it fast, secure and simple. All customization can be done through the command line interface however, so basic aliases could also be utilized to change default options. @@ -147,9 +147,9 @@ Ed van Bruggen <ed@edryd.org> ## See Also -See project page at <https://edryd.org/projects/gs.html> +See project page at <https://edryd.org/projects/gst.html> -View source code at <https://git.edryd.org/gs> +View source code at <https://git.edryd.org/gst> ## License diff --git a/config.mk b/config.mk @@ -1,4 +1,4 @@ -# gs version number +# gst version number VERSION = 0.0.0 # paths diff --git a/gs.1 b/gs.1 @@ -1,182 +0,0 @@ -.TH GS 1 "March 2020" "gs 0.0.0" -.PP -.SH NAME -gs \- painless gist creator -.PP -.SH SYNOPSIS -gs [-pPhv] [-e ID [-D FILE]] [-d DESCRIPTION] [-f FILENAME] [-g GITHUB_URL] [-u USER[:PASSWORD] | -U] FILES ... -.PP -.SH DESCRIPTION -.PP -Easy way to create GitHub Gists through the command line. Simply give it the files to upload and gs will return the URL to the new gist. -.PP -.SH OPTIONS -.TP -\fB-e \fP\fIID\fP -Edit previously created Gist specified with \fIID\fP -.PP -.TP -\fB-d \fP\fIDESCRIPTION\fP -Set Gist description -.PP -.TP -\fB-D \fP\fIFILE\fP -Delete \fIFILE\fP from Gist being edited -.PP -.TP -\fB-f \fP\fIFILENAME\fP -Set file name when reading from 'stdin' -.PP -.TP -\fB-g \fP\fIURL\fP -Change the GitHub API URL (default: https://api.github.com/gists) -.PP -.TP -\fB-p\fP -Make Gist private -.PP -.TP -\fB-P\fP -Make Gist public (default) -.PP -.TP -\fB-u \fP\fIUSER\fP:PASSWORD -Change the GitHub account the Gist will be posted under. A password can given as well with a separating colon, a prompt is provided if not. -.PP -.TP -\fB-U\fP -Post Gist anonymously (default) -.PP -.TP -\fB-h\fP -Print help and exit -.PP -.TP -\fB-v\fP -Print version info and exit -.PP -.SH USAGE -.PP -Create a new gist of 'file.txt': -.PP -.RS 4 -.EX - -$ gs file.txt -https://gist.github.com/<new-id> - -.EE -.RE -There is also support for multiple files in a single gist: -.PP -.RS 4 -.EX - -$ gs README.md Makefile prog.c -https://gist.github.com/<new-id> - -.EE -.RE -Specify the gist's description: -.PP -.RS 4 -.EX - -$ gs -d 'a cool shell script' script.sh -https://gist.github.com/<new-id> - -.EE -.RE -The gist's URL can be piped to other programs, for example to your clipboard to be pasted elsewhere: -.PP -.RS 4 -.EX - -$ gs file.txt | xsel -bi - -.EE -.RE -You can also create a new gist from 'stdin'. The file name needs to be supplied however: -.PP -.RS 4 -.EX - -$ cmd-which-errors | gs -f log.txt -https://gist.github.com/<new-id> - -.EE -.RE -The new gist can be posted under a GitHub user, a prompt will ask for your password: -.PP -.RS 4 -.EX - -$ gs -u your-name good-proj.rs -GitHub password: -https://gist.github.com/<new-id> - -.EE -.RE -To skip the prompt your password can be supplied after a colon with the username: -.PP -.RS 4 -.EX - -$ gs -u your-name:password plugin/func.vim doc/func.txt -https://gist.github.com/<new-id> - -.EE -.RE -New gists can be private when created under a user: -.PP -.RS 4 -.EX - -$ gs -p -u name personal.info -https://gist.github.com/<new-id> - -.EE -.RE -You can also edit a previously created gist under your account, such as adding a new file or updating the description: -.PP -.RS 4 -.EX - -$ gs -e <id> -u name -d "new description" additional-file.txt -https://gist.github.com/<id> - -.EE -.RE -It is even possible to delete files, although it should be noted the gist still exists even if you delete all the files within it due to the limitations of the API: -.PP -.RS 4 -.EX - -$ gs -e <id> -u name -D old.py -https://gist.github.com/<id> - -.EE -.RE -.SH INSTALLATION -.PP -The only dependency needed to build gs is cURL. gs be compiled with a simple 'make' and 'sudo make install' will install the executable and man page. -.PP -.SH CUSTOMIZATION -.PP -gs can be customized by editing the custom 'config.h' and (re)compiling the source code. This keeps it fast, secure and simple. All customization can be done through the command line interface however, so basic aliases could also be utilized to change default options. -.PP -.SH AUTHOR -.PP -Ed van Bruggen <ed@edryd.org> -.PP -.SH SEE ALSO -.PP -See project page at <https://edryd.org/projects/gs.html> -.PP -View source code at <https://git.edryd.org/gs> -.PP -.SH LICENSE -.PP -zlib License -.PP - diff --git a/gs.c b/gs.c @@ -1,230 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include <curl/curl.h> -#include <libgen.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "extern/arg.h" -#include "extern/frozen.h" -#include "util.h" - -/* defines */ -/* TODO remove BUF_SIZEs */ -#define LBUF_SIZE 1024 -#define BUF_SIZE 100000 -#define URL_SIZE 2048 - -/* typedefs */ -typedef struct { - char *ptr; - size_t len; -} Str; - -/* variables */ -char *argv0; - -#include "config.h" - -/* used by cURL to write its response to a Str */ -static size_t -str_write(void *ptr, size_t size, size_t nmemb, Str *s) -{ - size_t nlen = s->len + size*nmemb; - s->ptr = erealloc(s->ptr, nlen+1); - memcpy(s->ptr+s->len, ptr, size*nmemb); - s->ptr[nlen] = '\0'; - s->len = nlen; - - return size*nmemb; -} - -/* HTTP POST request with content, returning response */ -static Str -http_post(char *content) -{ - char *resmsg, url[URL_SIZE]; - long code; - CURL *curl; - CURLcode res; - Str resstr; - - resstr.len = 0; - resstr.ptr = emalloc(resstr.len+1); - resstr.ptr[0] = '\0'; - - /* init cURL */ - curl_global_init(CURL_GLOBAL_ALL); - if (!(curl = curl_easy_init())) { - curl_global_cleanup(); - die(1, "%s: cURL: could not init", argv0); - } - - /* set cURL options */ - if (gist) - snprintf(url, sizeof(url), "%s/%s", ghurl, gist); - else - strcpy(url, ghurl); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "gs/"VERSION); - if (user) { - if (strchr(user, ':')) { - curl_easy_setopt(curl, CURLOPT_USERPWD, user); - } else { - curl_easy_setopt(curl, CURLOPT_USERNAME, user); - curl_easy_setopt(curl, CURLOPT_PASSWORD, getpass("GitHub password: ")); - } - } - if (gist) - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, str_write); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resstr); - - /* run cURL */ - if ((res = curl_easy_perform(curl)) != CURLE_OK) { - curl_global_cleanup(); - die(1, "%s: cURL: %s", argv0, curl_easy_strerror(res)); - } - - /* response checking and cleanup */ - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - curl_easy_cleanup(curl); - curl_global_cleanup(); - /* 200 returned when editing gist, 201 when creating */ - if (code != (gist ? 200 : 201)) { - json_scanf(resstr.ptr, resstr.len, "{message: %Q}", &resmsg); - die(-1, "%s: [%d] could not create Gist: %s", - argv0, code, resmsg); - free(resmsg); - exit(1); - } - - return resstr; -} - -/* read file fp into returned string */ -static char * -file_str(FILE *fp) -{ - char lbuf[LBUF_SIZE]; /* buffer for each line */ - char *str = ecalloc(LBUF_SIZE, sizeof(char)); /* complete file */ - long flen = 1; /* file length */ - - /* loop through each line in the file, append it to str */ - while (fgets(lbuf, LBUF_SIZE, fp)) { - flen += strlen(lbuf); - str = erealloc(str, flen); - strcat(str, lbuf); - } - - return str; -} - -/* turn list of file names into returned json string */ -static char * -files_js(char *files[], int filec) -{ - char *fbuf; /* file contents */ - char *js = emalloc(BUF_SIZE*sizeof(char)); /* json string returned */ - FILE *fp = stdin; /* read from stdin by default if no file is given */ - struct json_out jout = JSON_OUT_BUF(js, BUF_SIZE); - - json_printf(&jout, "{ public: %B,", pub); - if (!desc && !gist) /* when creating new gist if no description given make it empty */ - desc = ""; - if (desc) /* only add description as blank if gist is being created */ - json_printf(&jout, "description: %Q, ", desc); - json_printf(&jout, "files: {"); - - if (del) /* delete file if given by setting it to NULL */ - json_printf(&jout, "%Q: %Q", basename(del), NULL); - - /* add each file */ - for (int i = 0; !i || i < filec; i++) { - if (filec && !(fp = fopen(files[i], "r"))) - die(1, "%s: %s: could not load file", argv0, files[i]); - if (filec) /* set file name if given */ - fname = files[i]; - if (!fname && gist) /* don't need file if we are editing gist */ - break; - if (!fname) /* check for file name when using stdin */ - die(1, "%s: file name not given", argv0); - if (i || del) /* insert comma if this is another file */ - json_printf(&jout, ","); - fbuf = file_str(fp); - json_printf(&jout, "%Q: { content: %Q }", basename(fname), fbuf); - free(fbuf); - } - - json_printf(&jout, "} }"); - - return js; -} - -static void -usage(const int eval) -{ - die(eval, "usage: %s [-pPhv] [-e ID [-D FILE]] [-d DESCRIPTION] [-f FILENAME]\n" - " [-g URL] [-u USER[:PASSWORD] | -U] FILES ...", argv0); -} - -int -main(int argc, char *argv[]) -{ - char *js, *url; - Str resstr; - - ARGBEGIN { - case 'e': - gist = EARGF(usage(1)); - break; - case 'd': - desc = EARGF(usage(1)); - break; - case 'D': - del = EARGF(usage(1)); - break; - case 'f': - fname = EARGF(usage(1)); - break; - case 'g': - ghurl = EARGF(usage(1)); - break; - case 'p': - pub = 0; - break; - case 'P': - pub = 1; - break; - case 'u': - user = EARGF(usage(1)); - break; - case 'U': - user = NULL; - break; - case 'h': - usage(0); - case 'v': - printf("%s v%s (c) 2017 Ed van Bruggen\n", argv0, VERSION); - return 0; - default: - usage(1); - } ARGEND; - - if (gist && !user) - die(1, "%s: cannot edit Gists without user", argv0); - - js = files_js(argv, argc); - resstr = http_post(js); - - json_scanf(resstr.ptr, resstr.len, "{html_url: %Q}", &url); - puts(url); - - free(js); - free(url); - free(resstr.ptr); - - return 0; -} diff --git a/gst.1 b/gst.1 @@ -0,0 +1,182 @@ +.TH GST 1 "April 2020" "gst 0.0.0" +.PP +.SH NAME +gst \- painless gist creator +.PP +.SH SYNOPSIS +gst [-pPhv] [-e ID [-D FILE]] [-d DESCRIPTION] [-f FILENAME] [-g URL] [-u USER[:PASSWORD] | -U] FILES ... +.PP +.SH DESCRIPTION +.PP +Easy way to create GitHub Gists through the command line. Simply give it the files to upload and gst will return the URL to the new gist. +.PP +.SH OPTIONS +.TP +\fB-e \fP\fIID\fP +Edit previously created gist specified with \fIID\fP +.PP +.TP +\fB-d \fP\fIDESCRIPTION\fP +Set gist description +.PP +.TP +\fB-D \fP\fIFILE\fP +Delete \fIFILE\fP from gist being edited +.PP +.TP +\fB-f \fP\fIFILENAME\fP +Set file name when reading from 'stdin' +.PP +.TP +\fB-g \fP\fIURL\fP +Change the GitHub API URL (default: https://api.github.com/gists) +.PP +.TP +\fB-p\fP +Make gist private +.PP +.TP +\fB-P\fP +Make gist public (default) +.PP +.TP +\fB-u \fP\fIUSER\fP:PASSWORD +Change the GitHub account the gist will be posted under. A password can given as well with a separating colon, a prompt is provided if not. +.PP +.TP +\fB-U\fP +Post gist anonymously (default) +.PP +.TP +\fB-h\fP +Print help and exit +.PP +.TP +\fB-v\fP +Print version info and exit +.PP +.SH USAGE +.PP +Create a new gist of 'file.txt': +.PP +.RS 4 +.EX + +$ gst file.txt +https://gist.github.com/<new-id> + +.EE +.RE +There is also support for multiple files in a single gist: +.PP +.RS 4 +.EX + +$ gst README.md Makefile prog.c +https://gist.github.com/<new-id> + +.EE +.RE +Specify the gist's description: +.PP +.RS 4 +.EX + +$ gst -d 'a cool shell script' script.sh +https://gist.github.com/<new-id> + +.EE +.RE +The gist's URL can be piped to other programs, for example to your clipboard to be pasted elsewhere: +.PP +.RS 4 +.EX + +$ gst file.txt | xsel -bi + +.EE +.RE +You can also create a new gist from 'stdin'. The file name needs to be supplied however: +.PP +.RS 4 +.EX + +$ cmd-which-errors | gst -f log.txt +https://gist.github.com/<new-id> + +.EE +.RE +The new gist can be posted under a GitHub user, a prompt will ask for your password: +.PP +.RS 4 +.EX + +$ gst -u your-name good-proj.rs +GitHub password: +https://gist.github.com/<new-id> + +.EE +.RE +To skip the prompt your password can be supplied after a colon with the username: +.PP +.RS 4 +.EX + +$ gst -u your-name:password plugin/func.vim doc/func.txt +https://gist.github.com/<new-id> + +.EE +.RE +New gists can be private when created under a user: +.PP +.RS 4 +.EX + +$ gst -p -u name personal.info +https://gist.github.com/<new-id> + +.EE +.RE +You can also edit a previously created gist under your account, such as adding a new file or updating the description: +.PP +.RS 4 +.EX + +$ gst -e <id> -u name -d "new description" additional-file.txt +https://gist.github.com/<id> + +.EE +.RE +It is even possible to delete files, although it should be noted the gist still exists even if you delete all the files within it due to the limitations of the API: +.PP +.RS 4 +.EX + +$ gst -e <id> -u name -D old.py +https://gist.github.com/<id> + +.EE +.RE +.SH INSTALLATION +.PP +The only dependency needed to build gst is cURL. gst be compiled with a simple 'make' and 'sudo make install' will install the executable and man page. +.PP +.SH CUSTOMIZATION +.PP +gst can be customized by editing the custom 'config.h' and (re)compiling the source code. This keeps it fast, secure and simple. All customization can be done through the command line interface however, so basic aliases could also be utilized to change default options. +.PP +.SH AUTHOR +.PP +Ed van Bruggen <ed@edryd.org> +.PP +.SH SEE ALSO +.PP +See project page at <https://edryd.org/projects/gst.html> +.PP +View source code at <https://git.edryd.org/gst> +.PP +.SH LICENSE +.PP +zlib License +.PP + diff --git a/gst.c b/gst.c @@ -0,0 +1,231 @@ +/* See LICENSE file for copyright and license details. */ +#include <curl/curl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern/arg.h" +#include "extern/frozen.h" +#include "util.h" + +/* defines */ +/* TODO remove BUF_SIZEs */ +#define LBUF_SIZE 1024 +#define BUF_SIZE 100000 +#define URL_SIZE 2048 + +/* typedefs */ +typedef struct { + char *ptr; + size_t len; +} Str; + +/* variables */ +char *argv0; + +#include "config.h" + +/* used by cURL to write its response to a Str */ +static size_t +str_write(void *ptr, size_t size, size_t nmemb, Str *s) +{ + size_t nlen = s->len + size*nmemb; + s->ptr = erealloc(s->ptr, nlen+1); + memcpy(s->ptr+s->len, ptr, size*nmemb); + s->ptr[nlen] = '\0'; + s->len = nlen; + + return size*nmemb; +} + +/* HTTP POST request with content, returning response */ +static Str +http_post(char *content) +{ + char *resmsg, url[URL_SIZE]; + long code; + CURL *curl; + CURLcode res; + Str resstr; + + resstr.len = 0; + resstr.ptr = emalloc(resstr.len+1); + resstr.ptr[0] = '\0'; + + /* init cURL */ + curl_global_init(CURL_GLOBAL_ALL); + if (!(curl = curl_easy_init())) { + curl_global_cleanup(); + die(1, "%s: cURL: could not init", argv0); + } + + /* set cURL options */ + if (gist) + snprintf(url, sizeof(url), "%s/%s", ghurl, gist); + else + strcpy(url, ghurl); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "gst/"VERSION); + if (user) { + if (strchr(user, ':')) { + curl_easy_setopt(curl, CURLOPT_USERPWD, user); + } else { + curl_easy_setopt(curl, CURLOPT_USERNAME, user); + curl_easy_setopt(curl, CURLOPT_PASSWORD, getpass("GitHub password: ")); + } + } + if (gist) + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, str_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resstr); + + /* run cURL */ + if ((res = curl_easy_perform(curl)) != CURLE_OK) { + curl_global_cleanup(); + die(1, "%s: cURL: %s", argv0, curl_easy_strerror(res)); + } + + /* response checking and cleanup */ + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + curl_easy_cleanup(curl); + curl_global_cleanup(); + /* 200 returned when editing gist, 201 when creating */ + if (code != (gist ? 200 : 201)) { + json_scanf(resstr.ptr, resstr.len, "{message: %Q}", &resmsg); + die(-1, "%s: [%d] could not create gist: %s", + argv0, code, resmsg); + free(resmsg); + exit(1); + } + + return resstr; +} + +/* read file fp into returned string */ +static char * +file_str(FILE *fp) +{ + char lbuf[LBUF_SIZE]; /* buffer for each line */ + char *str = ecalloc(LBUF_SIZE, sizeof(char)); /* complete file */ + long flen = 1; /* file length */ + + /* loop through each line in the file, append it to str */ + while (fgets(lbuf, LBUF_SIZE, fp)) { + flen += strlen(lbuf); + str = erealloc(str, flen); + strcat(str, lbuf); + } + + return str; +} + +/* turn list of file names into returned json string */ +static char * +files_js(char *files[], int filec) +{ + char *fbuf; /* file contents */ + char *js = emalloc(BUF_SIZE*sizeof(char)); /* json string returned */ + FILE *fp = stdin; /* read from stdin by default if no file is given */ + struct json_out jout = JSON_OUT_BUF(js, BUF_SIZE); + + json_printf(&jout, "{ public: %B,", pub); + if (!desc && !gist) /* when creating new gist if no description given make it empty */ + desc = ""; + if (desc) /* only add description as blank if gist is being created */ + json_printf(&jout, "description: %Q, ", desc); + json_printf(&jout, "files: {"); + + if (del) /* delete file if given by setting it to NULL */ + json_printf(&jout, "%Q: %Q", basename(del), NULL); + + /* add each file */ + for (int i = 0; !i || i < filec; i++) { + if (filec && !(fp = fopen(files[i], "r"))) + die(1, "%s: %s: could not load file", argv0, files[i]); + if (filec) /* set file name if given */ + fname = files[i]; + if (!fname && gist) /* don't need file if we are editing gist */ + break; + if (!fname) /* check for file name when using stdin */ + die(1, "%s: file name not given", argv0); + if (i || del) /* insert comma if this is another file */ + json_printf(&jout, ","); + fbuf = file_str(fp); + json_printf(&jout, "%Q: { content: %Q }", basename(fname), fbuf); + fclose(fp); + free(fbuf); + } + + json_printf(&jout, "} }"); + + return js; +} + +static void +usage(const int eval) +{ + die(eval, "usage: %s [-pPhv] [-e ID [-D FILE]] [-d DESCRIPTION] [-f FILENAME]\n" + " [-g URL] [-u USER[:PASSWORD] | -U] FILES ...", argv0); +} + +int +main(int argc, char *argv[]) +{ + char *js, *url; + Str resstr; + + ARGBEGIN { + case 'e': + gist = EARGF(usage(1)); + break; + case 'd': + desc = EARGF(usage(1)); + break; + case 'D': + del = EARGF(usage(1)); + break; + case 'f': + fname = EARGF(usage(1)); + break; + case 'g': + ghurl = EARGF(usage(1)); + break; + case 'p': + pub = 0; + break; + case 'P': + pub = 1; + break; + case 'u': + user = EARGF(usage(1)); + break; + case 'U': + user = NULL; + break; + case 'h': + usage(0); + case 'v': + printf("%s v%s (c) 2017 Ed van Bruggen\n", argv0, VERSION); + return 0; + default: + usage(1); + } ARGEND; + + if (gist && !user) + die(1, "%s: cannot edit gists without user", argv0); + + js = files_js(argv, argc); + resstr = http_post(js); + + json_scanf(resstr.ptr, resstr.len, "{html_url: %Q}", &url); + puts(url); + + free(js); + free(url); + free(resstr.ptr); + + return 0; +}