├── makefile ├── README.md ├── LICENSE └── z39-demo.c /makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | YAZCONFIG=~/Downloads/yaz_5.32.0.orig/yaz-5.32.0/yaz-config 3 | CFLAGS=`$(YAZCONFIG) --cflags` 4 | LIBS=`$(YAZCONFIG) --libs` 5 | z39-demo: z39-demo.o 6 | $(CC) $(CFLAGS) -o z39-demo z39-demo.o $(LIBS) 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # z39-demo 2 | 3 | This is a small demo command line C program for Z39.50 queries using Index Data's YAZ Z39.50 C toolkit: 4 | 5 | https://github.com/indexdata/yaz 6 | 7 | This z39-demo program was written by Cyrus Gomes, UA Libraries Data and Informatics Student Assistant. 8 | Please note that this software is an experiment program written for demo and teaching purposes. It has not been thoroughly tested and may contain bugs. 9 | 10 | The YAZ toolkit is not distributed with this program, users must obtain and install it separately. For more information about how to compile 11 | and run the z39-demo program, see The University of Alabama Libraries Scholarly API Cookbook Z39.50 page: 12 | 13 | https://ua-libraries-research-data-services.github.io/UALIB_ScholarlyAPI_Cookbook/src/shell/z3950.html 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 University of Alabama Libraries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /z39-demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // written by Cyrus Gomes 8 | // Other contributors: Vincent Scalfani and Avery Fernandez 9 | // Jan 18, 2023 10 | // MIT License, see repository details 11 | 12 | // 4 external functions 13 | // marc_create 14 | // uses an input query and saves MARC raw into a output file 15 | 16 | // marc_create_terminal 17 | // uses default UA catalog, an input query and renders the MARC ASCII as stdout 18 | 19 | // marc_create_server 20 | // uses an input query w/ custom Z39.50 address and saves MARC raw into a output file 21 | 22 | // marc_create_server_terminal 23 | // uses an input query w/ custom Z39.50 address, renders MARC ASCII to stdout 24 | 25 | // The following function outputs the MARC record in the 26 | // output file in binary with UA Libraries as default connection 27 | // default server address and uses input query to retrieve results 28 | // and create an output marc file 29 | 30 | void marc_create(char* query, int num, char* custom_f_name, int num_results){ 31 | char input[100]; // filename is 100 characters long (max) 32 | if (strcmp(custom_f_name, "\0") == 0){ 33 | char input[100] = "output"; 34 | char number[20]; // number of iteration is 20 charaters long. (e.g out1, out2, out3...) 35 | sprintf(number, "%d", num); 36 | strcat(input, number); 37 | strcat(input, ".marc"); 38 | 39 | } 40 | 41 | else{ 42 | char input[100] = {}; 43 | strcat(input,custom_f_name); 44 | } 45 | 46 | 47 | char *filename = input; // for file name 48 | FILE *fp = fopen(filename, "w"); // for creating the custom file 49 | 50 | // setting up the connection with the server 51 | ZOOM_connection z = ZOOM_connection_new ("library.ua.edu:7090/voyager", 0); 52 | ZOOM_resultset r; // records the result set after retrieving it from the query 53 | const char *rec; // a string that contains the individual marc record 54 | 55 | ZOOM_connection_option_set(z, "preferredRecordSyntax", "USMARC"); 56 | 57 | r = ZOOM_connection_search_pqf(z, query); 58 | 59 | int nums = num_results; 60 | 61 | if (nums>0){ 62 | if (nums >= ZOOM_resultset_size(r)){ //number of records in a single query 63 | nums = ZOOM_resultset_size(r); 64 | } 65 | 66 | else nums = nums; 67 | } 68 | 69 | else { 70 | nums = ZOOM_resultset_size(r); 71 | } 72 | 73 | for (int i = 0; i < nums; i++){ 74 | rec = ZOOM_record_get (ZOOM_resultset_record (r, i), "raw", 0); // records in a binary format 75 | fprintf(fp, "%s", rec); 76 | } 77 | fclose(fp); 78 | 79 | } 80 | 81 | 82 | 83 | 84 | // The following marc_create_terminal function outputs the queries using a default server address 85 | // and updates the result to the terminal in MARC ASCII format 86 | 87 | void marc_create_terminal(char* query, int num, int num_results){ 88 | 89 | ZOOM_connection z = ZOOM_connection_new ("library.ua.edu:7090/voyager", 0); // connection with the server 90 | ZOOM_resultset r; // records the result set after retrieving it from the query 91 | const char *rec; // a string that contains the individual marc record 92 | 93 | ZOOM_connection_option_set(z, "preferredRecordSyntax", "USMARC"); 94 | r = ZOOM_connection_search_pqf(z, query); 95 | int nums = num_results; 96 | 97 | if (nums>0){ 98 | if (nums >= ZOOM_resultset_size(r)){ //number of records in a single query 99 | nums = ZOOM_resultset_size(r); 100 | } 101 | 102 | else nums = nums; 103 | } 104 | 105 | else { 106 | nums = ZOOM_resultset_size(r); 107 | } 108 | 109 | for (int i = 0; i < nums; i++){ 110 | rec = ZOOM_record_get (ZOOM_resultset_record (r, i), "render", 0); // gets all the records in ASCII format 111 | printf("%s", rec); 112 | } 113 | 114 | } 115 | 116 | //The following marc_create_server function outputs the queries using a custom server address 117 | // and updates the result to the custom marc file 118 | 119 | void marc_create_server(char* query, int num, char* server, char* custom_f_name, int num_results){ 120 | char input[100]; // filename is 100 characters long (max) 121 | 122 | if (strcmp(custom_f_name, "\0") == 0){ 123 | char input[100] = "output"; 124 | char number[20]; // number of iteration is 20 charaters long. (e.g out1, out2, out3...) 125 | sprintf(number, "%d", num); 126 | strcat(input, number); 127 | strcat(input, ".marc"); 128 | } 129 | 130 | else{ 131 | char input[100] = {}; 132 | strcat(input,custom_f_name); 133 | } 134 | 135 | char *filename = input; 136 | FILE *fp = fopen(filename, "w"); 137 | 138 | ZOOM_connection z = ZOOM_connection_new (server, 0); 139 | ZOOM_resultset r; 140 | const char *rec; 141 | 142 | ZOOM_connection_option_set(z, "preferredRecordSyntax", "USMARC"); 143 | 144 | r = ZOOM_connection_search_pqf(z, query); 145 | int nums = num_results; 146 | 147 | if (nums>0){ 148 | if (nums >= ZOOM_resultset_size(r)){ //number of records in a single query 149 | nums = ZOOM_resultset_size(r); 150 | } 151 | 152 | else nums = nums; 153 | } 154 | 155 | else { 156 | nums = ZOOM_resultset_size(r); 157 | } 158 | 159 | for (int i = 0; i < nums; i++){ 160 | rec = ZOOM_record_get (ZOOM_resultset_record (r, i), "raw", 0); // gets all the records in a binary format 161 | fprintf(fp, "%s", rec); 162 | } 163 | 164 | fclose(fp); 165 | 166 | 167 | } 168 | 169 | //The following function outputs the marc record in the terminal in ASCII with custom server address 170 | 171 | void marc_create_server_terminal(char* query, int num, char* server, int num_results){ 172 | char input[100] = "output"; 173 | char number[20]; 174 | sprintf(number, "%d", num); 175 | strcat(input, number); 176 | strcat(input, ".marc"); 177 | 178 | ZOOM_connection z = ZOOM_connection_new (server, 0); 179 | ZOOM_resultset r; 180 | const char *rec; 181 | 182 | ZOOM_connection_option_set(z, "preferredRecordSyntax", "USMARC"); 183 | r = ZOOM_connection_search_pqf(z, query); 184 | int nums = num_results; 185 | 186 | if (nums>0){ 187 | if (nums >= ZOOM_resultset_size(r)){ //number of records in a single query 188 | nums = ZOOM_resultset_size(r); 189 | } 190 | 191 | else nums = nums; 192 | } 193 | 194 | else { 195 | nums = ZOOM_resultset_size(r); 196 | } 197 | 198 | for (int i = 0; i < nums; i++){ 199 | rec = ZOOM_record_get (ZOOM_resultset_record (r, i), "render", 0); // gets all the records in ASCII format 200 | printf("%s", rec); 201 | } 202 | 203 | } 204 | 205 | int main (int argc, char **argv){ 206 | 207 | FILE * fp; 208 | char * line = NULL; 209 | size_t len = 0; 210 | ssize_t read; 211 | int num = 0; 212 | 213 | if (argc == 1){ 214 | printf("See ./z39-demo -h \n"); 215 | return 1; 216 | } 217 | 218 | if (strcmp(argv[1],"-q") == 0){ 219 | 220 | if(argc == 3){ 221 | 222 | marc_create_terminal(argv[2], num, 0); 223 | 224 | } 225 | 226 | else if (argc == 4 && strcmp(argv[3],"-n")==0){ 227 | marc_create_terminal(argv[2], num, 0); 228 | } 229 | 230 | else if(argc == 5 && strcmp(argv[3],"-n") == 0){ 231 | marc_create_terminal(argv[2], num, atoi(argv[4])); 232 | } 233 | 234 | else if (argc == 4 && strcmp(argv[3],"-o")==0){ 235 | marc_create(argv[2], num, "\0", 0); 236 | } 237 | 238 | else if (argc == 5 && strcmp(argv[3],"-o")==0 && strcmp(argv[4],"-n") == 0){ 239 | marc_create(argv[2], num, "\0", 0); 240 | } 241 | 242 | else if (argc == 6 && strcmp(argv[3],"-o")==0 && strcmp(argv[4],"-n") == 0){ 243 | marc_create(argv[2], num, "\0", atoi(argv[5])); 244 | } 245 | 246 | else if (argc == 5 && strcmp(argv[3],"-o")==0){ 247 | marc_create(argv[2], num, argv[4], 0); 248 | } 249 | 250 | else if (argc == 6 && strcmp(argv[3],"-o")==0 && strcmp(argv[5],"-n") == 0){ 251 | marc_create(argv[2], num, argv[4], 0); 252 | } 253 | 254 | else if (argc == 7 && strcmp(argv[3],"-o")==0 && strcmp(argv[5],"-n") == 0){ 255 | marc_create(argv[2], num, argv[4], atoi(argv[6])); 256 | } 257 | 258 | else { 259 | printf("Please seek help!\n"); 260 | return 0; 261 | } 262 | } 263 | 264 | else if (strcmp(argv[1],"-h") == 0 || strcmp(argv[1],"-help") == 0){ 265 | printf("usage: myprogram [-h] [-z] [-o] -q/-i FILE\n\n"); 266 | printf("z39-demo is a command line program that can run Z39.50 searches via an input query or an input file\n\n"); 267 | printf("positional arguments:\n"); 268 | printf("\t -q required query for Z39.50 search\n"); 269 | printf("\t -i FILE input file with queries for Z39.50 with one per line\n"); 270 | printf("optional arguments\n"); 271 | printf("\t -h, -help show help and exit\n"); 272 | printf("\t -z optional custom Z39.50 adress; default is University of Alabama Libraries Catalog\n"); 273 | printf("\t -o FILE optional to output binary MARC file (FILE 100 chars max.); default is to print to stdout in MARC ASCII format \n"); 274 | printf("\t -n number optional to output specified number of results; default is to print the maximum number of results \n"); 275 | } 276 | 277 | else if (strcmp(argv[1],"-i") == 0){ // we test to see if input file is being used 278 | 279 | fp = fopen(argv[2], "r"); // the specified input file is then opened 280 | 281 | if (fp == NULL){ 282 | printf("Hello, something is wrong! :)"); 283 | return 0; 284 | } 285 | 286 | if(argc == 3){ 287 | while ((read = getline(&line, &len, fp)) != -1) { 288 | marc_create_terminal(line, num, 0); 289 | num++; 290 | sleep(5); // delay between queries 291 | 292 | if (num > 999){ //sets limit of the number of queries to 1000 293 | break; 294 | } 295 | } 296 | } 297 | 298 | else if(argc == 4 && strcmp(argv[3], "-n")==0){ 299 | while ((read = getline(&line, &len, fp)) != -1) { 300 | marc_create_terminal(line, num, 0); 301 | num++; 302 | sleep(5); // delay between queries 303 | 304 | if (num > 999){ //sets limit of the number of queries to 1000 305 | break; 306 | } 307 | } 308 | } 309 | 310 | else if(argc == 5 && strcmp(argv[3], "-n")==0){ 311 | while ((read = getline(&line, &len, fp)) != -1) { 312 | marc_create_terminal(line, num, atoi(argv[4])); 313 | num++; 314 | sleep(5); // delay between queries 315 | 316 | if (num > 999){ //sets limit of the number of queries to 1000 317 | break; 318 | } 319 | } 320 | } 321 | 322 | // continue work from here 323 | 324 | else if(argc == 4 && strcmp(argv[3],"-o")==0){ 325 | while ((read = getline(&line, &len, fp)) != -1) { 326 | marc_create(line, num, "\0", 0); 327 | num++; 328 | sleep(5); 329 | 330 | if (num > 999){ //sets limit of the number of queries to 1000 331 | break; 332 | } 333 | } 334 | } 335 | 336 | else if(argc == 5 && strcmp(argv[3],"-o")==0 && strcmp(argv[4], "-n")==0){ 337 | while ((read = getline(&line, &len, fp)) != -1) { 338 | marc_create(line, num, "\0", 0); 339 | num++; 340 | sleep(5); 341 | 342 | if (num > 999){ //sets limit of the number of queries to 1000 343 | break; 344 | } 345 | } 346 | } 347 | 348 | 349 | else if(argc == 6 && strcmp(argv[3],"-o")==0 && strcmp(argv[4], "-n")==0){ 350 | while ((read = getline(&line, &len, fp)) != -1) { 351 | marc_create(line, num, "\0", atoi(argv[5])); 352 | num++; 353 | sleep(5); 354 | 355 | if (num > 999){ //sets limit of the number of queries to 1000 356 | break; 357 | } 358 | } 359 | } 360 | 361 | 362 | fclose(fp); 363 | 364 | if (line) 365 | free(line); 366 | 367 | return 0; 368 | } 369 | 370 | //here on again 371 | 372 | else if (strcmp(argv[1],"-z") == 0){ //we test to see if the server is changed 373 | 374 | if (strcmp(argv[3],"-q") == 0){ 375 | 376 | if(argc == 5){ 377 | 378 | marc_create_server_terminal(argv[4], num, argv[2], 0); 379 | 380 | } 381 | 382 | else if (argc == 6 && strcmp(argv[5], "-n") == 0){ 383 | marc_create_server_terminal(argv[4], num, argv[2], 0); 384 | } 385 | 386 | else if (argc == 7 && strcmp(argv[5], "-n") == 0){ 387 | marc_create_server_terminal(argv[4], num, argv[2], atoi(argv[6])); 388 | } 389 | 390 | else if (argc == 6 && strcmp(argv[5],"-o")==0){ 391 | marc_create_server(argv[4], num, argv[2], "\0", 0); 392 | } 393 | 394 | else if (argc == 7 && strcmp(argv[5],"-o")==0 && strcmp(argv[6], "-n")==0){ 395 | marc_create_server(argv[4], num, argv[2], "\0", 0); 396 | } 397 | 398 | else if (argc == 8 && strcmp(argv[5],"-o")==0 && strcmp(argv[6], "-n")==0){ 399 | marc_create_server(argv[4], num, argv[2], "\0", atoi(argv[7])); 400 | } 401 | 402 | else if (argc == 7 && strcmp(argv[5],"-o")==0){ 403 | marc_create_server(argv[4], num, argv[2], argv[6], 0); 404 | } 405 | 406 | else if (argc == 8 && strcmp(argv[5],"-o")==0 && strcmp(argv[7], "-n") == 0){ 407 | marc_create_server(argv[4], num, argv[2], argv[6], 0); 408 | } 409 | 410 | else if (argc == 9 && strcmp(argv[5],"-o")==0 && strcmp(argv[7], "-n") == 0){ 411 | marc_create_server(argv[4], num, argv[2], argv[6], atoi(argv[8])); 412 | } 413 | 414 | } 415 | 416 | else if (strcmp(argv[3],"-i") == 0){ //we test to see if input file is being used 417 | 418 | fp = fopen(argv[4], "r"); 419 | 420 | if (fp == NULL){ 421 | printf("Hello, something is wrong! :)"); 422 | return 0; 423 | } 424 | 425 | if(argc == 5){ 426 | while ((read = getline(&line, &len, fp)) != -1) { 427 | marc_create_server_terminal(line, num, argv[2], 0); 428 | num++; 429 | sleep(5); 430 | 431 | if (num > 999){ //sets limit of the number of queries to 1000 432 | break; 433 | } 434 | } 435 | } 436 | 437 | else if(argc == 6 && strcmp(argv[5],"-n")==0){ 438 | while ((read = getline(&line, &len, fp)) != -1) { 439 | marc_create_server_terminal(line, num, argv[2], 0); 440 | num++; 441 | sleep(5); 442 | 443 | if (num > 999){ //sets limit of the number of queries to 1000 444 | break; 445 | } 446 | } 447 | } 448 | 449 | else if(argc == 7 && strcmp(argv[5],"-n")==0){ 450 | while ((read = getline(&line, &len, fp)) != -1) { 451 | marc_create_server_terminal(line, num, argv[2], atoi(argv[6])); 452 | num++; 453 | sleep(5); 454 | 455 | if (num > 999){ //sets limit of the number of queries to 1000 456 | break; 457 | } 458 | } 459 | } 460 | 461 | 462 | else if(argc == 6 && strcmp(argv[5],"-o")==0){ 463 | while ((read = getline(&line, &len, fp)) != -1) { 464 | marc_create_server(line, num, argv[2], "\0", 0); 465 | num++; 466 | sleep(5); 467 | 468 | if (num > 999){ //sets limit of the number of queries to 1000 469 | break; 470 | } 471 | } 472 | } 473 | 474 | else if(argc == 7 && strcmp(argv[5],"-o")==0 && strcmp(argv[6], "-n") == 0){ 475 | while ((read = getline(&line, &len, fp)) != -1) { 476 | marc_create_server(line, num, argv[2], "\0", 0); 477 | num++; 478 | sleep(5); 479 | 480 | if (num > 999){ //sets limit of the number of queries to 1000 481 | break; 482 | } 483 | } 484 | } 485 | 486 | else if(argc == 8 && strcmp(argv[5],"-o")==0 && strcmp(argv[6], "-n") == 0){ 487 | while ((read = getline(&line, &len, fp)) != -1) { 488 | marc_create_server(line, num, argv[2], "\0", atoi(argv[7])); 489 | num++; 490 | sleep(5); 491 | 492 | if (num > 999){ //sets limit of the number of queries to 1000 493 | break; 494 | } 495 | } 496 | } 497 | 498 | 499 | 500 | fclose(fp); 501 | 502 | if (line) 503 | free(line); 504 | 505 | return 0; 506 | } 507 | 508 | } 509 | 510 | } --------------------------------------------------------------------------------