{"record":{"id":"b9f8260ff1ab0e87","repo":"Yalantis/uCrop","slug":"cimg-appname-math-parser-cimg-s-s-unter","errorCode":null,"errorMessage":"[\" cimg_appname \"_math_parser] CImg<%s>::%s: Unterminated string literal, in expression '%s'.","messagePattern":"\\[\" cimg_appname \"_math_parser\\] CImg<(.+?)>::(.+?): Unterminated string literal, in expression '(.+?)'\\.","errorType":"exception","errorClass":"CImgArgumentException","httpStatus":null,"severity":"error","filePath":"ucrop/src/main/jni/CImg.h","lineNumber":25016,"sourceCode":"        int _level = 0;\n        for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {\n          if (!is_escaped && !next_is_escaped && *ps=='\\\\') next_is_escaped = true;\n          if (!is_escaped && *ps=='\\'') { // Non-escaped character\n            if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string\n            else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string\n            else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string\n          }\n          *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):\n                                   *ps=='(' || *ps=='['?_level++:\n                                   *ps==')' || *ps==']'?--_level:\n                                   _level);\n          mode = next_mode;\n          is_escaped = next_is_escaped;\n          next_is_escaped = false;\n        }\n        if (mode) {\n          cimg::strellipsize(_expr,64);\n          throw CImgArgumentException(\"[\" cimg_appname \"_math_parser] \"\n                                      \"CImg<%s>::%s: Unterminated string literal, in expression '%s'.\",\n                                      pixel_type(),_cimg_mp_calling_function,\n                                      _expr._data);\n        }\n        if (_level) {\n          cimg::strellipsize(_expr,64);\n          throw CImgArgumentException(\"[\" cimg_appname \"_math_parser] \"\n                                      \"CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.\",\n                                      pixel_type(),_cimg_mp_calling_function,\n                                      _expr._data);\n        }\n        return res;\n      }\n\n      // Find and return index of current image 'imgin' within image list 'imglist'.\n      unsigned int get_mem_img_index() {\n        if (mem_img_index==~0U) {\n          if (&imgout>=imglist.data() && &imgout<imglist.end())","sourceCodeStart":24998,"sourceCodeEnd":25034,"githubUrl":"https://github.com/Yalantis/uCrop/blob/f788b534b48c144edf786c8cddbf0e029e637804/ucrop/src/main/jni/CImg.h#L24998-L25034","documentation":"CImg's math expression parser (used by functions like fill() with an expression string, or expression-based image operations) scans the expression for string literals delimited by quotes. If the parser reaches the end of input while still inside a quoted string (mode != 0), it throws CImgArgumentException reporting an unterminated string literal, truncating the expression to 64 chars via strellipsize for readability.","triggerScenarios":"Passing a math expression string to a CImg function (e.g. img.fill(\"...\") or an expression in a formula-based call) where an opening quote (' or \") is never closed, e.g. fill(\"if(x>2,'hello',0)\") with a missing closing quote, or a quote accidentally escaped/eaten by an outer shell or JSON layer.","commonSituations":"Building expressions programmatically or in shell scripts where quotes get stripped by the shell or a JSON/ini config layer; typos in G'MIC-style expressions; copy-pasting expressions where smart quotes or one of a matched pair is lost.","solutions":["Inspect the expression string at runtime (log it) and find the opening quote that is never closed","Ensure every string literal inside the expression has a matching closing quote of the same character","If the expression passes through a shell, JSON, or XML layer, verify quotes survive escaping (double them or use the layer's escape syntax)","Split the expression into smaller pieces and test each in isolation to locate the malformed literal"],"exampleFix":"// before\nimg.fill(\"if(x>2,'odd',0)\"); // fine\nimg.fill(\"if(x>2,'odd,0)\");  // unterminated string literal\n// after\nimg.fill(\"if(x>2,'odd',0)\");","handlingStrategy":"validation","validationCode":"// C++: check quote balance before passing expression to CImg\nbool quotes_balanced(const std::string& e) {\n  int sq = 0, dq = 0;\n  for (size_t i = 0; i < e.size(); ++i) {\n    if (i > 0 && e[i-1] == '\\\\') continue; // skip escaped\n    if (e[i] == '\\'') sq ^= 1;\n    if (e[i] == '\"') dq ^= 1;\n  }\n  return sq == 0 && dq == 0;\n}\nif (!quotes_balanced(expr)) throw std::invalid_argument(\"unterminated string literal in expression\");","typeGuard":"bool has_balanced_quotes(const std::string& e); // see validationCode","tryCatchPattern":"try {\n  img.fill(expr);\n} catch (const cimg_library::CImgArgumentException& e) {\n  std::cerr << \"Bad expression: \" << e.what() << \" | expr=\" << expr << std::endl;\n}","preventionTips":["Log the final expression string before every CImg call","Never build quoted literals inside expressions by hand-concatenation; use a helper","Beware shell/JSON layers stripping quotes; test with a fixed literal first"],"tags":["cimg","math-parser","expression-parsing","string-literal"],"backgroundTag":"invalid-argument-format","analyzedSha":"f788b534b48c144edf786c8cddbf0e029e637804","analyzedAt":"2026-09-08T08:36:04.887Z","contentChangedAt":"2026-09-08T08:36:04.887Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}