+ gsTextArgs[0] = "";
+
+ // -- Create regular expressions
+ // > Code Type Count ...
+ CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); //
+ // > Code Type Ident Status Price Desc
+ CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+(avail|sold|error)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED);
+ // > Code 'SALT' salt
+ CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+(.+)$", REG_EXTENDED);
+ // > Code 'User' Username Balance Flags
+ CompileRegex(&gUserInfoRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([^ ]+)\\s+(-?[0-9]+)\\s+(.+)$", REG_EXTENDED);
+ // > Item Ident
+ CompileRegex(&gUserItemIdentRegex, "^([A-Za-z]+):([0-9]+)$", REG_EXTENDED);
+
+ // Parse Arguments
+ ret = ParseArguments(argc, argv);
+ if( ret )
+ return ret;
+
+ // Sub-commands
+ if( strcmp(gsTextArgs[0], "finger") == 0 ) {
+ return subcommand_finger();
+ }
+ else if( strcmp(gsTextArgs[0], "acct") == 0 ) {
+ return subcommand_acct();
+ }
+ else if( strcmp(gsTextArgs[0], "give") == 0 ) {
+ return subcommand_give(giTextArgc-1, gsTextArgs+1);
+ }
+ else if( strcmp(gsTextArgs[0], "user") == 0 ) {
+ return subcommand_user(giTextArgc-1, gsTextArgs+1);
+ }
+ else if( strcmp(gsTextArgs[0], "donate") == 0 ) {
+ return subcommand_donate(giTextArgc-1, gsTextArgs+1);
+ }
+ else if( strcmp(gsTextArgs[0], "refund") == 0 ) {
+ return subcommand_refund(giTextArgc-1, gsTextArgs+1);
+ }
+ else if( strcmp(gsTextArgs[0], "iteminfo") == 0 ) {
+ return subcommand_iteminfo(giTextArgc-1, gsTextArgs+1);
+ }
+ else if( strcmp(gsTextArgs[0], "slot") == 0 ) {
+ return subcommand_slot(giTextArgc-1, gsTextArgs+1);
+ }
+ else if(strcmp(gsTextArgs[0], "pincheck") == 0) {
+ return subcommand_pincheck(giTextArgc-1, gsTextArgs+1);
+ }
+ else if(strcmp(gsTextArgs[0], "pinset") == 0) {
+ return subcommand_pinset(giTextArgc-1, gsTextArgs+1);
+ }
+ else {
+ // Item name / pattern
+ gsItemPattern = gsTextArgs[0];
+ }
+
+ // Connect to server
+ int sock = OpenConnection(gsDispenseServer, giDispensePort);
+ if( sock < 0 ) return RV_SOCKET_ERROR;
+
+ // Get the user's balance
+ ret = GetUserBalance(sock);
+ if(ret) return ret;
+
+ // Get items
+ PopulateItemList(sock);
+
+ // Disconnect from server
+ close(sock);
+
+ if( gsItemPattern && gsItemPattern[0] )
+ {
+ regmatch_t matches[3];
+ // Door (hard coded)
+ if( strcmp(gsItemPattern, "door") == 0 )
+ {
+ // Connect, Authenticate, dispense and close
+ sock = OpenConnection(gsDispenseServer, giDispensePort);
+ if( sock < 0 ) return RV_SOCKET_ERROR;
+ ret = Authenticate(sock);
+ if(ret) return ret;
+ ret = DispenseItem(sock, "door", 0);
+ close(sock);
+ return ret;
+ }
+ // Item id (<type>:<num>)
+ else if( RunRegex(&gUserItemIdentRegex, gsItemPattern, 3, matches, NULL) == 0 )
+ {
+ char *ident;
+ int id;
+
+ // Get and finish ident
+ ident = gsItemPattern + matches[1].rm_so;
+ gsItemPattern[matches[1].rm_eo] = '\0';
+ // Get ID
+ id = atoi( gsItemPattern + matches[2].rm_so );
+
+ // Connect, Authenticate, dispense and close
+ sock = OpenConnection(gsDispenseServer, giDispensePort);
+ if( sock < 0 ) return RV_SOCKET_ERROR;
+
+ Dispense_ItemInfo(sock, ident, id);
+
+ ret = Authenticate(sock);
+ if(ret) return ret;
+ ret = DispenseItem(sock, ident, id);
+ close(sock);
+ return ret;
+ }
+ // Item number (6 = coke)
+ else if( strcmp(gsItemPattern, "0") == 0 || atoi(gsItemPattern) > 0 )
+ {
+ i = atoi(gsItemPattern);
+ }
+ // Item prefix
+ else
+ {
+ int j;
+ int best = -1;
+ for( i = 0; i < giNumItems; i ++ )
+ {
+ // Prefix match (with case-insensitive match)
+ for( j = 0; gsItemPattern[j]; j ++ )
+ {
+ if( gaItems[i].Desc[j] == gsItemPattern[j] )
+ continue;
+ if( tolower(gaItems[i].Desc[j]) == tolower(gsItemPattern[j]) )
+ continue;
+ break;
+ }
+ // Check if the prefix matched
+ if( gsItemPattern[j] != '\0' )
+ continue;
+
+ // Prefect match
+ if( gaItems[i].Desc[j] == '\0' ) {
+ best = i;
+ break;
+ }
+
+ // Only one match allowed
+ if( best == -1 ) {
+ best = i;
+ }
+ else {
+ // TODO: Allow ambiguous matches?
+ // or just print a wanrning
+ printf("Warning - Ambiguous pattern, stopping\n");
+ return RV_BAD_ITEM;
+ }
+ }
+
+ // Was a match found?
+ if( best == -1 )
+ {
+ fprintf(stderr, "No item matches the passed string\n");
+ return RV_BAD_ITEM;
+ }
+
+ i = best;
+ }
+ }
+ else if( giUIMode != UI_MODE_BASIC )
+ {
+ i = ShowNCursesUI();
+ }
+ else
+ {
+ // Very basic dispense interface
+ for( i = 0; i < giNumItems; i ++ ) {
+ // Add a separator
+ if( i && strcmp(gaItems[i].Type, gaItems[i-1].Type) != 0 )
+ printf(" ---\n");
+
+ printf("%2i %s:%i\t%3i %s\n", i, gaItems[i].Type, gaItems[i].ID,
+ gaItems[i].Price, gaItems[i].Desc);
+ }
+ printf(" q Quit\n");
+ for(;;)
+ {
+ char *buf;
+
+ i = -1;
+
+ fgets(buffer, BUFSIZ, stdin);
+
+ buf = trim(buffer);
+
+ if( buf[0] == 'q' ) break;
+
+ i = atoi(buf);
+
+ if( i != 0 || buf[0] == '0' )
+ {
+ if( i < 0 || i >= giNumItems ) {
+ printf("Bad item %i (should be between 0 and %i)\n", i, giNumItems);
+ continue;
+ }
+ break;
+ }
+ }