int cfg_write(Config cfg, FILE* f) { ConfigEntry entry; if(f == NULL) { fprintf(stderr, "Config: file pointer is NULL\n"); return CFG_STREAM_ERROR; } entry = cfg->first; while(entry != NULL) { if(needs_quotes(entry->value)) fprintf(f, "%s = \"%s\"\n", entry->key, entry->value); else fprintf(f, "%s = %s\n", entry->key, entry->value); entry = entry->next; } return CFG_OKAY; }
/* ==================== Prompt_CompleteCommand ==================== */ void Prompt_CompleteCommand(commandPrompt_t *prompt, qboolean backslash) { inputField_t *inputLine = &prompt->inputLine; char *text, *partial, *s; int i, argc, currentArg, argnum; size_t size, len, pos; char *first, *last; genctx_t ctx; char *matches[MAX_MATCHES], *sortedMatches[MAX_MATCHES]; int numCommands, numCvars, numAliases; text = inputLine->text; size = inputLine->maxChars + 1; pos = inputLine->cursorPos; // prepend backslash if missing if (backslash) { if (inputLine->text[0] != '\\' && inputLine->text[0] != '/') { memmove(inputLine->text + 1, inputLine->text, size - 1); inputLine->text[0] = '\\'; } text++; size--; pos--; } // parse the input line into tokens Cmd_TokenizeString(text, qfalse); argc = Cmd_Argc(); // determine absolute argument number to be completed currentArg = Cmd_FindArgForOffset(pos); if (currentArg == argc - 1 && Cmd_WhiteSpaceTail()) { // start completing new argument if command line has trailing whitespace currentArg++; } // determine relative argument number to be completed argnum = 0; for (i = 0; i < currentArg; i++) { s = Cmd_Argv(i); argnum++; if (*s == ';') { // semicolon starts a new command argnum = 0; } } // get the partial argument string to be completed partial = Cmd_Argv(currentArg); if (*partial == ';') { // semicolon starts a new command currentArg++; partial = Cmd_Argv(currentArg); argnum = 0; } // generate matches memset(&ctx, 0, sizeof(ctx)); ctx.partial = partial; ctx.length = strlen(partial); ctx.argnum = currentArg; ctx.matches = matches; ctx.size = MAX_MATCHES; if (argnum) { // complete a command/cvar argument Com_Generic_c(&ctx, argnum); numCommands = numCvars = numAliases = 0; } else { // complete a command/cvar/alias name Cmd_Command_g(&ctx); numCommands = ctx.count; Cvar_Variable_g(&ctx); numCvars = ctx.count - numCommands; Cmd_Alias_g(&ctx); numAliases = ctx.count - numCvars - numCommands; } if (!ctx.count) { pos = strlen(inputLine->text); prompt->tooMany = qfalse; goto finish2; // nothing found } pos = Cmd_ArgOffset(currentArg); text += pos; size -= pos; // append whitespace since Cmd_TokenizeString eats it if (currentArg == argc && Cmd_WhiteSpaceTail()) { *text++ = ' '; pos++; size--; } if (ctx.count == 1) { // we have finished completion! s = Cmd_RawArgsFrom(currentArg + 1); if (needs_quotes(matches[0])) { pos += Q_concat(text, size, "\"", matches[0], "\" ", s, NULL); } else { pos += Q_concat(text, size, matches[0], " ", s, NULL); } pos++; prompt->tooMany = qfalse; goto finish1; } if (ctx.count > com_completion_treshold->integer && !prompt->tooMany) { prompt->printf("Press TAB again to display all %d possibilities.\n", ctx.count); pos = strlen(inputLine->text); prompt->tooMany = qtrue; goto finish1; } prompt->tooMany = qfalse; // sort matches alphabethically for (i = 0; i < ctx.count; i++) { sortedMatches[i] = matches[i]; } qsort(sortedMatches, ctx.count, sizeof(sortedMatches[0]), ctx.ignorecase ? SortStricmp : SortStrcmp); // copy matching part first = sortedMatches[0]; last = sortedMatches[ctx.count - 1]; len = 0; do { if (*first != *last) { if (!ctx.ignorecase || Q_tolower(*first) != Q_tolower(*last)) { break; } } text[len++] = *first; if (len == size - 1) { break; } first++; last++; } while (*first); text[len] = 0; pos += len; size -= len; // copy trailing arguments if (currentArg + 1 < argc) { s = Cmd_RawArgsFrom(currentArg + 1); pos += Q_concat(text + len, size, " ", s, NULL); } pos++; prompt->printf("]\\%s\n", Cmd_ArgsFrom(0)); if (argnum) { goto multi; } switch (com_completion_mode->integer) { case 0: // print in solid list for (i = 0; i < ctx.count; i++) { prompt->printf("%s\n", sortedMatches[i]); } break; case 1: multi: // print in multiple columns Prompt_ShowMatches(prompt, sortedMatches, 0, ctx.count); break; case 2: default: // resort matches by type and print in multiple columns Prompt_ShowIndividualMatches(prompt, matches, numCommands, numAliases, numCvars); break; } finish1: // free matches for (i = 0; i < ctx.count; i++) { Z_Free(matches[i]); } finish2: // move cursor if (pos >= inputLine->maxChars) { pos = inputLine->maxChars - 1; } inputLine->cursorPos = pos; }