2 * Copyright (c) 2016, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include <curl/curl.h>
26 #include <yajl_parse.h>
31 /* Upper bound on length of an acceptable config file line */
32 #define LINE_BUF_LEN (1 << 11)
34 /* How many bytes to read at once during CSV parsing */
35 #define READ_BUF_LEN (1 << 10)
40 CPS_IN_NONQUOTED_CELL
,
49 curl_to_devnull(char *ptr
, size_t size
, size_t nmemb
, void *ctx
);
51 get_config_file_contents(const char *config_file_name
);
53 extract_next_link(char *buffer
, size_t size
, size_t nitems
, void *userdata
);
55 ie_yc_number(void *ctx
, const char *val
, size_t len
);
57 ie_yc_map_key(void *ctx
, const unsigned char *key
, size_t len
);
59 ie_yc_string(void *ctx
, const unsigned char *val
, size_t len
);
61 kve_yc_boolean(void *ctx
, int val
);
63 kve_yc_end_map(void *ctx
);
65 kve_yc_map_key(void *ctx
, const unsigned char *key
, size_t len
);
67 kve_yc_start_map(void *ctx
);
69 kve_yc_string(void *ctx
, const unsigned char *val
, size_t len
);
71 kve_yc_number(void *ctx
, const char *nval
, size_t len
);
73 map_hash(const char *s
);
74 yajl_callbacks ie_callbacks
= {
76 .yajl_number
= ie_yc_number
, /* */
77 .yajl_map_key
= ie_yc_map_key
, /* */
78 .yajl_string
= ie_yc_string
, /* */
80 yajl_callbacks kve_callbacks
= {
82 .yajl_end_map
= kve_yc_end_map
, /* */
83 .yajl_map_key
= kve_yc_map_key
, /* */
84 .yajl_start_map
= kve_yc_start_map
, /* */
85 .yajl_string
= kve_yc_string
, /* */
86 .yajl_number
= kve_yc_number
, /* */
87 .yajl_boolean
= kve_yc_boolean
/* */
89 static CURL
* create_curl_handle(void)
91 CURL
*c
= curl_easy_init();
97 curl_easy_setopt(c
, CURLOPT_USERAGENT
, NCI_USERAGENT
);
98 curl_easy_setopt(c
, CURLOPT_FOLLOWLOCATION
, 1L);
99 curl_easy_setopt(c
, CURLOPT_NOPROGRESS
, 1L);
100 curl_easy_setopt(c
, CURLOPT_MAXREDIRS
, 10L);
101 curl_easy_setopt(c
, CURLOPT_WRITEFUNCTION
, curl_to_devnull
);
102 curl_easy_setopt(c
, CURLOPT_WRITEDATA
, (void *) 0);
107 static size_t curl_to_devnull(char *ptr
, size_t size
, size_t nmemb
, void *ctx
)
117 static size_t curl_to_yajl(char *ptr
, size_t size
, size_t nmemb
, void *ctx
)
119 size_t total
= size
* nmemb
;
120 curl_to_yajl_ctx
*c
= (curl_to_yajl_ctx
*) ctx
;
122 unsigned char *ye
= 0;
125 * As part of some security thing I don't give a damn about,
126 * Canvas prepends all their responses with `while(1);' or
129 if (!c
->skipped_chars
) {
140 c
->skipped_chars
= 1;
146 ys
= yajl_parse(c
->yh
, (const unsigned char *) ptr
, size
* nmemb
);
148 if (ys
== yajl_status_client_canceled
||
149 ys
== yajl_status_error
) {
150 ye
= yajl_get_error(c
->yh
, 1, (const unsigned char *) ptr
,
152 fprintf(stderr
, "yajl: %s\n", ye
);
153 yajl_free_error(c
->yh
, ye
);
161 static size_t extract_next_link(char *buffer
, size_t size
, size_t nitems
,
164 size_t total
= size
* nitems
;
165 char **storage_location
= (char **) userdata
;
168 char *terminus
= buffer
+ (total
- 1);
171 for (p
= buffer
; p
< terminus
- 13; ++p
) {
174 } else if (!strncmp(p
, ">; rel=\"next\"", 13) &&
176 if (!(dup
= strndup(lt
+ 1, (p
- lt
) - 1))) {
180 free(*storage_location
);
181 *storage_location
= dup
;
188 char * get_auth_token(void)
190 return get_config_file_contents("token");
193 static char * get_config_file_contents(const char *config_file_name
)
195 char *xdg_config_home
= getenv("XDG_CONFIG_HOME");
196 char *home
= getenv("HOME");
200 char *extra_newline
= 0;
203 char buf
[LINE_BUF_LEN
];
205 size_t bytes_read
= 0;
206 uint_fast8_t eof
= 0;
208 if (!xdg_config_home
||
209 !xdg_config_home
[0]) {
211 fprintf(stderr
, "You don't have a $HOME. I give up.\n");
215 len
= snprintf(0, 0, "%s/.config/nci/%s", home
,
218 if (!(path
= malloc(len
+ 1))) {
223 sprintf(path
, "%s/.config/nci/%s", home
, config_file_name
);
225 len
= snprintf(0, 0, "%s/nci/%s", xdg_config_home
,
228 if (!(path
= malloc(len
+ 1))) {
233 sprintf(path
, "%s/nci/%s", xdg_config_home
, config_file_name
);
236 if (!(f
= fopen(path
, "r"))) {
238 fprintf(stderr
, "Could not open %s\n", path
);
243 if (buf
+ (LINE_BUF_LEN
- 1) <= b
) {
245 "File %s is too long - refusing to use it\n",
250 bytes_read
= fread(b
, 1, (buf
+ (LINE_BUF_LEN
- 1) - b
), f
);
264 fprintf(stderr
, "File %s is empty - it shouldn't be\n", path
);
268 if (!(contents
= malloc((b
- buf
) + 1))) {
273 sprintf(contents
, "%s", buf
);
275 if ((extra_newline
= strchr(contents
, '\n'))) {
276 *extra_newline
= '\0';
290 char * get_url_base(void)
292 return get_config_file_contents("site");
295 static int ie_yc_number(void *ctx
, const char *val
, size_t len
)
297 return ie_yc_string(ctx
, (const unsigned char *) val
, len
);
300 static int ie_yc_map_key(void *ctx
, const unsigned char *key
, size_t len
)
302 ie_y_ctx
*c
= (ie_y_ctx
*) ctx
;
304 c
->saw_id_key
= !strncmp((const char *) key
, "id", 2) &&
306 c
->saw_message_key
= !strncmp((const char *) key
, "message", 7);
311 static int ie_yc_string(void *ctx
, const unsigned char *val
, size_t len
)
313 ie_y_ctx
*c
= (ie_y_ctx
*) ctx
;
316 if (!c
->saw_id_key
&&
317 !c
->saw_message_key
) {
326 if (!(dup
= strndup((const char *) val
, len
))) {
327 perror(L("strndup"));
336 c
->error_message
= dup
;
342 int key_value_extract(const char *original_path
, const char *auth_token
, const
343 char **field_names
, char *enclosing_id
, map
*m
)
347 yajl_handle y
= { 0 };
348 kve_y_ctx ct
= { 0 };
350 curl_to_yajl_ctx ctyc
= { 0 };
352 char ce
[CURL_ERROR_SIZE
];
354 char *free_after_next_perform
= 0;
356 char *auth_header
= 0;
358 struct curl_slist
*custom_headers
= 0;
359 const char *path
= original_path
;
363 if (!(c
= create_curl_handle())) {
364 ret
= errno
= ENOMEM
;
365 perror("create_curl_handle");
369 len
= snprintf(0, 0, "Authorization: Bearer %s", auth_token
);
371 if (!(auth_header
= malloc(len
+ 1))) {
377 sprintf(auth_header
, "Authorization: Bearer %s", auth_token
);
379 if (!(custom_headers
= curl_slist_append(custom_headers
,
385 perror(L("curl_slist_append"));
389 curl_easy_setopt(c
, CURLOPT_HTTPHEADER
, custom_headers
);
390 curl_easy_setopt(c
, CURLOPT_WRITEFUNCTION
, curl_to_yajl
);
391 curl_easy_setopt(c
, CURLOPT_WRITEDATA
, (void *) &ctyc
);
392 curl_easy_setopt(c
, CURLOPT_ERRORBUFFER
, &ce
);
393 curl_easy_setopt(c
, CURLOPT_HEADERFUNCTION
, extract_next_link
);
394 curl_easy_setopt(c
, CURLOPT_HEADERDATA
, (void *) &next_uri
);
398 ct
= (kve_y_ctx
) { .m
= m
, .field_names
= field_names
,
399 .enclosing_id
= enclosing_id
};
401 if (!(y
= yajl_alloc(&kve_callbacks
, 0, (void *) &ct
))) {
402 ret
= errno
= ENOMEM
;
403 perror(L("yajl_alloc()"));
407 ctyc
= (curl_to_yajl_ctx
) { .yh
= y
};
408 curl_easy_setopt(c
, CURLOPT_URL
, path
);
409 curl_ret
= curl_easy_perform(c
);
410 curl_easy_getinfo(c
, CURLINFO_RESPONSE_CODE
, &http
);
411 yajl_complete_parse(y
);
418 free(free_after_next_perform
);
422 if (ct
.otherfields
) {
423 for (j
= 1; field_names
[j
]; ++j
) {
424 free(ct
.otherfields
[j
- 1]);
428 free(ct
.otherfields
);
431 if (curl_ret
!= CURLE_OK
||
433 if (ct
.error_message
) {
434 fprintf(stderr
, "Error: %s\n",
438 fprintf(stderr
, "%s: %s (http %ld)\n\n%s\n", L(
439 "libcurl"), curl_easy_strerror(
449 free_after_next_perform
= next_uri
;
453 free(ct
.error_message
);
456 curl_easy_cleanup(c
);
459 curl_slist_free_all(custom_headers
);
471 static int kve_yc_end_map(void *ctx
)
473 kve_y_ctx
*c
= (kve_y_ctx
*) ctx
;
476 if (c
->exhausted_enclosing_id
) {
480 if (c
->depth
== c
->enclosing_id_depth
) {
481 c
->exhausted_enclosing_id
= 1;
484 if (c
->depth
-- != c
->enclosing_id_depth
+ 1) {
492 mret
= map_add(c
->m
, c
->field0
, c
->otherfields
);
499 static int kve_handle(kve_y_ctx
*c
, const char *s
, size_t l
)
501 int truncated_l
= (l
> 70) ? 70 : l
;
507 !c
->enclosing_id_depth
&&
508 l
== strlen(c
->enclosing_id
) &&
509 !strncmp(s
, c
->enclosing_id
, l
)) {
510 c
->enclosing_id_depth
= c
->depth
;
515 if (c
->enclosing_id
&&
516 (c
->exhausted_enclosing_id
||
517 !c
->enclosing_id_depth
)) {
521 if (c
->idx_of_next_entry
< 0 &&
522 !c
->saw_message_key
) {
526 dup
= malloc(truncated_l
+ 1);
534 snprintf(dup
, truncated_l
+ 1, "%s", s
);
536 if (c
->saw_message_key
) {
537 c
->error_message
= dup
;
542 switch (c
->idx_of_next_entry
) {
548 if (!c
->otherfields
) {
549 if (!c
->m
->num_values_per_entry
) {
550 for (j
= 0; c
->field_names
[j
]; ++j
) {
553 c
->m
->num_values_per_entry
= j
- 1;
556 if (!(c
->otherfields
= calloc(
557 c
->m
->num_values_per_entry
,
558 sizeof *c
->field_names
))) {
563 c
->otherfields
[c
->idx_of_next_entry
- 1] = dup
;
567 c
->idx_of_next_entry
= -1;
572 static int kve_yc_boolean(void *ctx
, int val
)
575 if (kve_handle((kve_y_ctx
*) ctx
, "true", 4)) {
579 if (kve_handle((kve_y_ctx
*) ctx
, "false", 5)) {
587 int kve_yc_map_key(void *ctx
, const unsigned char *key
, size_t len
)
589 kve_y_ctx
*c
= (kve_y_ctx
*) ctx
;
590 const char *s
= (const char *) key
;
593 c
->saw_message_key
= (!(strncmp(s
, "message", 7)) &&
595 c
->saw_id_key
= c
->enclosing_id
&&
597 (!(strncmp(s
, "id", 2))) &&
600 if (!strncmp(s
, c
->field_names
[0], len
)) {
601 c
->idx_of_next_entry
= 0;
605 for (j
= 1; c
->field_names
[j
]; ++j
) {
606 if (!strncmp((const char *) key
, c
->field_names
[j
],
608 c
->idx_of_next_entry
= j
;
615 c
->idx_of_next_entry
= -1;
620 static int kve_yc_number(void *ctx
, const char *nval
, size_t len
)
622 if (kve_handle((kve_y_ctx
*) ctx
, nval
, len
)) {
629 static int kve_yc_start_map(void *ctx
)
631 kve_y_ctx
*c
= (kve_y_ctx
*) ctx
;
638 static int kve_yc_string(void *ctx
, const unsigned char *val
, size_t len
)
640 kve_y_ctx
*c
= (kve_y_ctx
*) ctx
;
641 const char *s
= (const char *) val
;
643 if (kve_handle(c
, s
, len
)) {
650 int make_gse_post(grading_standard_entry
*cutoffs
, size_t cutoffs_num
, struct
651 curl_httppost
**out_post
)
653 struct curl_httppost
*post
= 0;
654 struct curl_httppost
*postend
= 0;
658 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
, "title",
659 CURLFORM_PTRCONTENTS
, "Grading Scheme",
661 ret
= errno
? errno
: ENOMEM
;
663 perror(L("curl_formadd"));
667 for (j
= 0; j
< cutoffs_num
; ++j
) {
668 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
669 "grading_scheme_entry[][name]",
670 CURLFORM_PTRCONTENTS
,
671 cutoffs
[j
].name
, CURLFORM_END
)) {
672 ret
= errno
? errno
: ENOMEM
;
674 perror(L("curl_formadd"));
678 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
679 "grading_scheme_entry[][value]",
680 CURLFORM_PTRCONTENTS
,
681 cutoffs
[j
].value
, CURLFORM_END
)) {
682 ret
= errno
? errno
: ENOMEM
;
684 perror(L("curl_formadd"));
702 int make_course_post(const char *hfg
, const char *aagw
, int
703 disable_grading_standard
, const char *start_date
, const
704 char *end_date
, const
705 char *gse_id
, struct curl_httppost
**out_post
)
707 struct curl_httppost
*post
= 0;
708 struct curl_httppost
*postend
= 0;
712 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
713 "course[hide_final_grades]",
714 CURLFORM_PTRCONTENTS
, hfg
,
716 ret
= errno
? errno
: ENOMEM
;
718 perror(L("curl_formadd"));
724 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
725 "course[apply_assignment_group_weights]",
726 CURLFORM_PTRCONTENTS
,
727 aagw
, CURLFORM_END
)) {
728 ret
= errno
? errno
: ENOMEM
;
730 perror(L("curl_formadd"));
735 if (disable_grading_standard
) {
736 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
737 "course[grading_standard_id]",
738 CURLFORM_PTRCONTENTS
, "",
740 ret
= errno
? errno
: ENOMEM
;
742 perror(L("curl_formadd"));
748 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
749 "course[start_at]", CURLFORM_PTRCONTENTS
,
752 ret
= errno
? errno
: ENOMEM
;
754 perror(L("curl_formadd"));
760 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
761 "course[end_at]", CURLFORM_PTRCONTENTS
,
764 ret
= errno
? errno
: ENOMEM
;
766 perror(L("curl_formadd"));
772 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
773 "course[grading_standard_id]",
774 CURLFORM_PTRCONTENTS
, gse_id
,
776 ret
= errno
? errno
: ENOMEM
;
778 perror(L("curl_formadd"));
796 struct curl_httppost
* make_agroup_post(const char *name
, const char *weight
,
797 const char *drop_lowest
, const
800 struct curl_httppost
*post
= 0;
801 struct curl_httppost
*postend
= 0;
804 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
, "name",
805 CURLFORM_PTRCONTENTS
, name
, CURLFORM_END
)) {
806 errno
= errno
? errno
: ENOMEM
;
807 perror(L("curl_formadd"));
813 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
814 "group_weight", CURLFORM_PTRCONTENTS
, weight
,
816 errno
= errno
? errno
: ENOMEM
;
817 perror(L("curl_formadd"));
823 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
824 "rules[drop_lowest]", CURLFORM_PTRCONTENTS
,
827 errno
= errno
? errno
: ENOMEM
;
828 perror(L("curl_formadd"));
834 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
835 "rules[drop_highest]", CURLFORM_PTRCONTENTS
,
838 errno
= errno
? errno
: ENOMEM
;
839 perror(L("curl_formadd"));
851 struct curl_httppost
* make_assignment_post(const char *name
, const
852 char *max_points
, const
853 char *due_date
, const
856 struct curl_httppost
*post
= 0;
857 struct curl_httppost
*postend
= 0;
859 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
860 "assignment[submission_types][]", CURLFORM_PTRCONTENTS
,
863 errno
= errno
? errno
: ENOMEM
;
864 perror(L("curl_formadd"));
868 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
869 "assignment[notify_of_update]", CURLFORM_PTRCONTENTS
,
872 errno
= errno
? errno
: ENOMEM
;
873 perror(L("curl_formadd"));
877 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
878 "assignment[grading_type]", CURLFORM_PTRCONTENTS
,
881 errno
= errno
? errno
: ENOMEM
;
882 perror(L("curl_formadd"));
886 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
887 "assignment[published]", CURLFORM_PTRCONTENTS
, "true",
889 errno
= errno
? errno
: ENOMEM
;
890 perror(L("curl_formadd"));
895 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
896 "assignment[name]", CURLFORM_PTRCONTENTS
, name
,
898 errno
= errno
? errno
: ENOMEM
;
899 perror(L("curl_formadd"));
905 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
906 "assignment[points_possible]",
907 CURLFORM_PTRCONTENTS
, max_points
,
909 errno
= errno
? errno
: ENOMEM
;
910 perror(L("curl_formadd"));
916 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
917 "assignment[due_at]", CURLFORM_PTRCONTENTS
,
920 errno
= errno
? errno
: ENOMEM
;
921 perror(L("curl_formadd"));
927 if (curl_formadd(&post
, &postend
, CURLFORM_PTRNAME
,
928 "assignment[assignment_group_id]",
929 CURLFORM_PTRCONTENTS
,
930 group_id
, CURLFORM_END
)) {
931 errno
= errno
? errno
: ENOMEM
;
932 perror(L("curl_formadd"));
944 struct curl_httppost
* make_update_grade_post(char ***csv
, size_t col
, size_t
947 struct curl_httppost
*post
= 0;
948 struct curl_httppost
*postend
= 0;
950 char *param_name
= 0;
951 size_t param_name_len
= 0;
954 for (j
= 1; j
< rows
; ++j
) {
963 if (!strcasecmp(csv
[j
][col
], "ex")) {
964 len
= snprintf(0, 0, "grade_data[%s][excuse]",
967 if (len
> param_name_len
) {
968 param_name_len
= len
+ 1;
970 if (!(param_name
= realloc(param_name
,
972 perror(L("realloc"));
977 sprintf(param_name
, "grade_data[%s][excuse]",
980 if (curl_formadd(&post
, &postend
, CURLFORM_COPYNAME
,
981 param_name
, CURLFORM_PTRCONTENTS
,
984 errno
= errno
? errno
: ENOMEM
;
985 perror(L("curl_formadd"));
989 len
= snprintf(0, 0, "grade_data[%s][posted_grade]",
992 if (len
> param_name_len
) {
993 param_name_len
= len
+ 1;
995 if (!(param_name
= realloc(param_name
,
997 perror(L("realloc"));
1002 sprintf(param_name
, "grade_data[%s][posted_grade]",
1005 if (curl_formadd(&post
, &postend
, CURLFORM_COPYNAME
,
1006 param_name
, CURLFORM_PTRCONTENTS
,
1009 errno
= errno
? errno
: ENOMEM
;
1010 perror(L("curl_formadd"));
1018 curl_formfree(post
);
1026 int map_add(map
*m
, char *k
, char **vs
)
1032 size_t hash
= map_hash(k
);
1041 for (j
= 0; j
< m
->es
[hash
]; ++j
) {
1044 if (!(strcmp(k
, e
->k
))) {
1048 for (l
= 0; l
< m
->num_values_per_entry
; ++l
) {
1060 if (!(newmem
= realloc(m
->e
[hash
], (m
->es
[hash
] + 1) *
1061 sizeof *m
->e
[hash
]))) {
1065 m
->e
[hash
] = newmem
;
1066 m
->e
[hash
][m
->es
[hash
]] = (entry
) { .k
= k
, .vs
= vs
};
1076 void map_clean(map
*m
)
1087 for (j
= 0; j
< BUCKET_NUM
; ++j
) {
1088 for (k
= 0; k
< m
->es
[j
]; ++k
) {
1093 for (l
= 0; l
< m
->num_values_per_entry
; ++l
) {
1107 static int map_cmp_key(const void *a
, const void *b
)
1109 const char *s
= *((const char **) a
);
1110 const char *t
= *((const char **) b
);
1111 long long m
= strtoll(s
, 0, 0);
1112 long long n
= strtoll(t
, 0, 0);
1114 return (m
< n
) ? -1 : ((m
> n
) ? 1 : strcmp(s
, t
));
1117 char ** map_get(map
*m
, char *k
)
1121 size_t hash
= map_hash(k
);
1128 for (j
= 0; j
< m
->es
[hash
]; ++j
) {
1131 if (!(strcmp(k
, e
->k
))) {
1139 void map_get_keys(map
*m
, char ***out_k
, size_t *out_num
)
1141 size_t num_keys
= 0;
1146 for (j
= 0; j
< BUCKET_NUM
; ++j
) {
1147 num_keys
+= m
->es
[j
];
1150 if (!(*out_k
= malloc((sizeof **out_k
) * num_keys
))) {
1156 for (j
= 0; j
< BUCKET_NUM
; ++j
) {
1157 for (k
= 0; k
< m
->es
[j
]; ++k
) {
1158 (*out_k
)[l
++] = m
->e
[j
][k
].k
;
1162 qsort(*out_k
, num_keys
, sizeof **out_k
, map_cmp_key
);
1163 *out_num
= num_keys
;
1166 static size_t map_hash(const char *s
)
1176 for (p
= s
, j
= 0; *p
&&
1178 hash
= hash
* 33 ^ *p
;
1181 return hash
% BUCKET_NUM
;
1184 void print_esc_0x22(const char *s
)
1188 /* Quote as per RFC 4180 */
1201 static int process_char_csv(int c
, csv_parse_state
*s
, char **cell
,
1202 size_t *cell_sz
, size_t *cell_pos
, char ****csv
,
1203 char *reading_header
,
1204 size_t *record_len
, size_t *y
, size_t *x
)
1208 if (*cell_pos
+ 1 >= *cell_sz
) {
1209 if (!(newmem
= realloc(*cell
, (*cell_sz
+ 64) *
1211 perror(L("realloc"));
1222 case CPS_START_OF_CELL
:
1225 *s
= CPS_END_OF_INPUT
;
1226 } else if (c
== '"') {
1227 *s
= CPS_IN_QUOTED_CELL
;
1228 } else if (c
== ',') {
1229 *s
= CPS_JUST_ENDED_CELL
;
1230 (*cell
)[*cell_pos
] = '\0';
1231 } else if (c
== '\n') {
1232 *s
= CPS_JUST_ENDED_LINE
;
1233 (*cell
)[*cell_pos
] = '\0';
1235 (*cell
)[(*cell_pos
)++] = (char) c
;
1236 *s
= CPS_IN_NONQUOTED_CELL
;
1240 case CPS_IN_QUOTED_CELL
:
1244 } else if (c
== '"') {
1245 *s
= CPS_JUST_SAW_0x22
;
1247 (*cell
)[(*cell_pos
)++] = (char) c
;
1251 case CPS_IN_NONQUOTED_CELL
:
1254 *s
= CPS_END_OF_INPUT
;
1255 (*cell
)[*cell_pos
] = '\0';
1256 } else if (c
== ',') {
1257 *s
= CPS_JUST_ENDED_CELL
;
1258 (*cell
)[*cell_pos
] = '\0';
1259 } else if (c
== '\n') {
1260 *s
= CPS_JUST_ENDED_LINE
;
1261 (*cell
)[*cell_pos
] = '\0';
1262 } else if (c
== '"') {
1265 (*cell
)[(*cell_pos
)++] = (char) c
;
1269 case CPS_JUST_SAW_0x22
:
1272 *s
= CPS_END_OF_INPUT
;
1273 } else if (c
== '"') {
1274 (*cell
)[(*cell_pos
)++] = '"';
1275 *s
= CPS_IN_QUOTED_CELL
;
1276 } else if (c
== ',') {
1277 *s
= CPS_JUST_ENDED_CELL
;
1278 (*cell
)[*cell_pos
] = '\0';
1279 } else if (c
== '\n') {
1280 *s
= CPS_JUST_ENDED_LINE
;
1281 (*cell
)[*cell_pos
] = '\0';
1287 case CPS_JUST_ENDED_LINE
:
1288 case CPS_JUST_ENDED_CELL
:
1291 *s
= CPS_END_OF_INPUT
;
1292 } else if (c
== '\n') {
1293 *s
= CPS_JUST_ENDED_LINE
;
1294 (*cell
)[*cell_pos
] = '\0';
1295 } else if (c
== ',') {
1296 *s
= CPS_JUST_ENDED_CELL
;
1297 (*cell
)[*cell_pos
] = '\0';
1298 } else if (c
== '"') {
1299 *s
= CPS_IN_QUOTED_CELL
;
1301 (*cell
)[(*cell_pos
)++] = (char) c
;
1302 *s
= CPS_IN_NONQUOTED_CELL
;
1307 case CPS_END_OF_INPUT
:
1311 if (*reading_header
&&
1312 (*s
== CPS_JUST_ENDED_CELL
||
1313 *s
== CPS_JUST_ENDED_LINE
||
1314 *s
== CPS_END_OF_INPUT
)) {
1317 if (*reading_header
) {
1318 if (!(newmem
= realloc((*csv
)[0], (*record_len
+ 1) *
1320 perror(L("realloc"));
1329 if (*s
== CPS_JUST_ENDED_CELL
||
1330 *s
== CPS_JUST_ENDED_LINE
||
1331 *s
== CPS_END_OF_INPUT
) {
1332 if (*x
>= *record_len
) {
1338 (*csv
)[*y
][*x
] = *cell
;
1340 if (!(*cell
= malloc(1 * sizeof **cell
))) {
1341 perror(L("malloc"));
1350 if (*s
== CPS_JUST_ENDED_LINE
) {
1353 *reading_header
= 0;
1355 if (!(newmem
= realloc(*csv
, (*y
+ 1) * sizeof *csv
))) {
1356 perror(L("realloc"));
1363 if (!((*csv
)[*y
] = calloc(*record_len
, sizeof ***csv
))) {
1364 perror(L("calloc"));
1370 if (*s
== CPS_JUST_ENDED_CELL
) {
1377 int read_csv(FILE *f
, char ****out_csv
, size_t *out_rows
, size_t *out_cols
)
1379 size_t fread_ret
= 0;
1380 csv_parse_state s
= CPS_START_OF_CELL
;
1383 size_t cell_pos
= 0;
1385 char reading_header
= 1;
1386 size_t record_len
= 0;
1391 char buf
[READ_BUF_LEN
];
1392 const char *buf_end
= 0;
1395 if (!(cell
= malloc(cell_sz
* sizeof *cell
))) {
1396 perror(L("malloc"));
1401 if (!(csv
= malloc(1 * sizeof *csv
))) {
1402 perror(L("malloc"));
1407 if (!(csv
[0] = malloc(1 * sizeof **csv
))) {
1408 perror(L("malloc"));
1417 fread_ret
= fread(buf
, sizeof *buf
, READ_BUF_LEN
, f
);
1423 buf_end
= buf
+ (fread_ret
- 1);
1426 while (p
<= buf_end
) {
1427 if (process_char_csv(*p
, &s
, &cell
, &cell_sz
, &cell_pos
,
1428 &csv
, &reading_header
, &record_len
,
1437 if (s
== CPS_INVALID
) {
1445 *out_cols
= record_len
;
1450 for (j
= 0; j
< y
; ++j
) {
1451 for (k
= 0; k
< x
; ++k
) {
1464 int send_and_id_scan(const char *uri
, const char *auth_token
, struct
1465 curl_httppost
*post
, const char *method
, char **id
)
1469 yajl_handle y
= { 0 };
1470 ie_y_ctx ct
= { .id_str
= id
};
1471 curl_to_yajl_ctx ctyc
= { 0 };
1473 char ce
[CURL_ERROR_SIZE
];
1475 char *auth_header
= 0;
1477 struct curl_slist
*custom_headers
= 0;
1481 if (!(c
= create_curl_handle())) {
1482 ret
= errno
= ENOMEM
;
1483 perror("create_curl_handle");
1487 len
= snprintf(0, 0, "Authorization: Bearer %s", auth_token
);
1489 if (!(auth_header
= malloc(len
+ 1))) {
1491 perror(L("malloc"));
1495 sprintf(auth_header
, "Authorization: Bearer %s", auth_token
);
1497 if (!(custom_headers
= curl_slist_append(custom_headers
,
1503 perror(L("curl_slist_append"));
1508 if (!(y
= yajl_alloc(&ie_callbacks
, 0, (void *) &ct
))) {
1509 ret
= errno
= ENOMEM
;
1510 perror(L("yajl_alloc()"));
1514 ctyc
= (curl_to_yajl_ctx
) { .yh
= y
};
1515 curl_easy_setopt(c
, CURLOPT_HTTPHEADER
, custom_headers
);
1516 curl_easy_setopt(c
, CURLOPT_HTTPPOST
, post
);
1517 curl_easy_setopt(c
, CURLOPT_CUSTOMREQUEST
, method
);
1518 curl_easy_setopt(c
, CURLOPT_WRITEFUNCTION
, curl_to_yajl
);
1519 curl_easy_setopt(c
, CURLOPT_WRITEDATA
, (void *) &ctyc
);
1520 curl_easy_setopt(c
, CURLOPT_ERRORBUFFER
, &ce
);
1521 curl_easy_setopt(c
, CURLOPT_URL
, uri
);
1522 curl_ret
= curl_easy_perform(c
);
1523 curl_easy_getinfo(c
, CURLINFO_RESPONSE_CODE
, &http
);
1524 yajl_complete_parse(y
);
1526 if (curl_ret
!= CURLE_OK
||
1528 if (ct
.error_message
) {
1529 fprintf(stderr
, L("Error: %s\n"), ct
.error_message
);
1532 fprintf(stderr
, "%s: %s (http %ld)\n\n%s\n", L(
1533 "libcurl"), curl_easy_strerror(
1534 curl_ret
), http
, ce
);
1540 free(ct
.error_message
);
1543 curl_easy_cleanup(c
);
1546 curl_slist_free_all(custom_headers
);