mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 04:22:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			444 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			444 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2020 The Libc Authors. All rights reserved. | |||
|  | // Use of this source code is governed by a BSD-style | |||
|  | // license that can be found in the LICENSE file. | |||
|  | 
 | |||
|  | package libc // import "modernc.org/libc" | |||
|  | 
 | |||
|  | import ( | |||
|  | 	"strings" | |||
|  | 	"unsafe" | |||
|  | ) | |||
|  | 
 | |||
|  | // The format string consists of a sequence of directives which describe how to | |||
|  | // process the sequence of input characters.  If processing of a directive | |||
|  | // fails, no further input  is  read,  and scanf()  returns.   A "failure" can | |||
|  | // be either of the following: input failure, meaning that input characters | |||
|  | // were unavailable, or matching failure, meaning that the input was | |||
|  | // inappropriate. | |||
|  | func scanf(r *strings.Reader, format, args uintptr) (nvalues int32) { | |||
|  | 	var src []byte //TODO- | |||
|  | 	var ok bool | |||
|  | out: | |||
|  | 	for { | |||
|  | 		c := *(*byte)(unsafe.Pointer(format)) | |||
|  | 		src = append(src, c) //TODO- | |||
|  | 		switch c { | |||
|  | 		case '%': | |||
|  | 			var n int | |||
|  | 			var match bool | |||
|  | 			format, n, match = scanfConversion(r, format, &args) | |||
|  | 			if !match { | |||
|  | 				break out | |||
|  | 			} | |||
|  | 
 | |||
|  | 			nvalues += int32(n) | |||
|  | 			ok = true | |||
|  | 		case 0: | |||
|  | 			break out | |||
|  | 		case ' ', '\t', '\n', '\r', '\v', '\f': | |||
|  | 			format = skipWhiteSpace(format) | |||
|  | 			ok = true | |||
|  | 		next: | |||
|  | 			for { | |||
|  | 				c, err := r.ReadByte() | |||
|  | 				if err != nil { | |||
|  | 					break out | |||
|  | 				} | |||
|  | 
 | |||
|  | 				switch c { | |||
|  | 				case ' ', '\t', '\n', '\r', '\v', '\f': | |||
|  | 					// nop | |||
|  | 				default: | |||
|  | 					r.UnreadByte() | |||
|  | 					break next | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			c2, err := r.ReadByte() | |||
|  | 			if err != nil { | |||
|  | 				break out | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if c2 != c { | |||
|  | 				r.UnreadByte() | |||
|  | 				break out | |||
|  | 			} | |||
|  | 
 | |||
|  | 			format++ | |||
|  | 			ok = true | |||
|  | 		} | |||
|  | 	} | |||
|  | 	if ok { | |||
|  | 		return nvalues | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return -1 // stdio.EOF but not defined for windows | |||
|  | } | |||
|  | 
 | |||
|  | func scanfConversion(r *strings.Reader, format uintptr, args *uintptr) (_ uintptr, nvalues int, match bool) { | |||
|  | 	format++ // '%' | |||
|  | 
 | |||
|  | 	// Each conversion specification in format begins with either the character '%' | |||
|  | 	// or the character sequence "%n$" (see below for the distinction) followed by: | |||
|  | 
 | |||
|  | 	mod := 0 | |||
|  | 	width := -1 | |||
|  | flags: | |||
|  | 	for { | |||
|  | 		switch c := *(*byte)(unsafe.Pointer(format)); c { | |||
|  | 		case '*': | |||
|  | 			// An  optional '*' assignment-suppression character: scanf() reads input as | |||
|  | 			// directed by the conversion specification, but discards the input.  No | |||
|  | 			// corresponding pointer argument is re‐ quired, and this specification is not | |||
|  | 			// included in the count of successful assignments returned by scanf(). | |||
|  | 			format++ | |||
|  | 			panic(todo("")) | |||
|  | 		case '\'': | |||
|  | 			// For decimal conversions, an optional quote character (').  This specifies | |||
|  | 			// that the input number may include thousands' separators as defined by the | |||
|  | 			// LC_NUMERIC category of  the  current locale.  (See setlocale(3).)  The quote | |||
|  | 			// character may precede or follow the '*' assignment-suppression character. | |||
|  | 			format++ | |||
|  | 			panic(todo("")) | |||
|  | 		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |||
|  | 			// An  optional  decimal  integer  which  specifies  the maximum field width. | |||
|  | 			// Reading of characters stops either when this maximum is reached or when a | |||
|  | 			// nonmatching character is found, whichever happens first.  Most conversions | |||
|  | 			// discard initial white space characters (the exceptions are noted below), and | |||
|  | 			// these discarded characters don't  count  toward  the  maximum field width. | |||
|  | 			// String input conversions store a terminating null byte ('\0') to mark the | |||
|  | 			// end of the input; the maximum field width does not include this terminator. | |||
|  | 			width = 0 | |||
|  | 		num: | |||
|  | 			for { | |||
|  | 				var digit int | |||
|  | 				switch c := *(*byte)(unsafe.Pointer(format)); { | |||
|  | 				default: | |||
|  | 					break num | |||
|  | 				case c >= '0' && c <= '9': | |||
|  | 					format++ | |||
|  | 					digit = int(c) - '0' | |||
|  | 				} | |||
|  | 				width0 := width | |||
|  | 				width = 10*width + digit | |||
|  | 				if width < width0 { | |||
|  | 					panic(todo("")) | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case 'h', 'j', 'l', 'L', 'q', 't', 'z': | |||
|  | 			format, mod = parseLengthModifier(format) | |||
|  | 		default: | |||
|  | 			break flags | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// A conversion specifier that specifies the type of input conversion to be | |||
|  | 	// performed. | |||
|  | 	switch c := *(*byte)(unsafe.Pointer(format)); c { | |||
|  | 	case '%': | |||
|  | 		// Matches a literal '%'.  That is, %% in the format string matches a single | |||
|  | 		// input '%' character.  No conversion is done (but initial white space | |||
|  | 		// characters are discarded), and assign‐ ment does not occur. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'd': | |||
|  | 		// Matches an optionally signed decimal integer; the next pointer must be a | |||
|  | 		// pointer to int. | |||
|  | 		format++ | |||
|  | 		skipReaderWhiteSpace(r) | |||
|  | 		var digit, n uint64 | |||
|  | 		allowSign := true | |||
|  | 		neg := false | |||
|  | 	dec: | |||
|  | 		for ; width != 0; width-- { | |||
|  | 			c, err := r.ReadByte() | |||
|  | 			if err != nil { | |||
|  | 				if match { | |||
|  | 					break dec | |||
|  | 				} | |||
|  | 
 | |||
|  | 				panic(todo("", err)) | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if allowSign { | |||
|  | 				switch c { | |||
|  | 				case '-': | |||
|  | 					allowSign = false | |||
|  | 					neg = true | |||
|  | 					continue | |||
|  | 				case '+': | |||
|  | 					allowSign = false | |||
|  | 					continue | |||
|  | 				} | |||
|  | 			} | |||
|  | 
 | |||
|  | 			switch { | |||
|  | 			case c >= '0' && c <= '9': | |||
|  | 				digit = uint64(c) - '0' | |||
|  | 			default: | |||
|  | 				r.UnreadByte() | |||
|  | 				break dec | |||
|  | 			} | |||
|  | 			match = true | |||
|  | 			n0 := n | |||
|  | 			n = n*10 + digit | |||
|  | 			if n < n0 { | |||
|  | 				panic(todo("")) | |||
|  | 			} | |||
|  | 		} | |||
|  | 		if !match { | |||
|  | 			break | |||
|  | 		} | |||
|  | 
 | |||
|  | 		arg := VaUintptr(args) | |||
|  | 		v := int64(n) | |||
|  | 		if neg { | |||
|  | 			v = -v | |||
|  | 		} | |||
|  | 		switch mod { | |||
|  | 		case modNone: | |||
|  | 			*(*int32)(unsafe.Pointer(arg)) = int32(v) | |||
|  | 		case modH: | |||
|  | 			*(*int16)(unsafe.Pointer(arg)) = int16(v) | |||
|  | 		case modHH: | |||
|  | 			*(*int8)(unsafe.Pointer(arg)) = int8(v) | |||
|  | 		case modL: | |||
|  | 			*(*long)(unsafe.Pointer(arg)) = long(n) | |||
|  | 		default: | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 		nvalues = 1 | |||
|  | 	case 'D': | |||
|  | 		// Equivalent  to  ld;  this  exists  only for backward compatibility.  (Note: | |||
|  | 		// thus only in libc4.  In libc5 and glibc the %D is silently ignored, causing | |||
|  | 		// old programs to fail mysteriously.) | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'i': | |||
|  | 		// Matches an optionally signed integer; the next pointer must be a pointer to | |||
|  | 		// int.  The integer is read in base 16 if it begins with 0x or 0X, in base 8 | |||
|  | 		// if it begins with  0,  and  in base 10 otherwise.  Only characters that | |||
|  | 		// correspond to the base are used. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'o': | |||
|  | 		// Matches an unsigned octal integer; the next pointer must be a pointer to | |||
|  | 		// unsigned int. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'u': | |||
|  | 		// Matches an unsigned decimal integer; the next pointer must be a pointer to | |||
|  | 		// unsigned int. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'x', 'X': | |||
|  | 		// Matches an unsigned hexadecimal integer; the next pointer must be a pointer | |||
|  | 		// to unsigned int. | |||
|  | 		format++ | |||
|  | 		skipReaderWhiteSpace(r) | |||
|  | 		var digit, n uint64 | |||
|  | 		allowPrefix := true | |||
|  | 		var b []byte | |||
|  | 	hex: | |||
|  | 		for ; width != 0; width-- { | |||
|  | 			c, err := r.ReadByte() | |||
|  | 			if err != nil { | |||
|  | 				if match { | |||
|  | 					break hex | |||
|  | 				} | |||
|  | 
 | |||
|  | 				panic(todo("", err)) | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if allowPrefix { | |||
|  | 				if len(b) == 1 && b[0] == '0' && (c == 'x' || c == 'X') { | |||
|  | 					allowPrefix = false | |||
|  | 					match = false | |||
|  | 					b = nil | |||
|  | 					continue | |||
|  | 				} | |||
|  | 
 | |||
|  | 				b = append(b, c) | |||
|  | 			} | |||
|  | 
 | |||
|  | 			switch { | |||
|  | 			case c >= '0' && c <= '9': | |||
|  | 				digit = uint64(c) - '0' | |||
|  | 			case c >= 'a' && c <= 'f': | |||
|  | 				digit = uint64(c) - 'a' + 10 | |||
|  | 			case c >= 'A' && c <= 'F': | |||
|  | 				digit = uint64(c) - 'A' + 10 | |||
|  | 			default: | |||
|  | 				r.UnreadByte() | |||
|  | 				break hex | |||
|  | 			} | |||
|  | 			match = true | |||
|  | 			n0 := n | |||
|  | 			n = n<<4 + digit | |||
|  | 			if n < n0 { | |||
|  | 				panic(todo("")) | |||
|  | 			} | |||
|  | 		} | |||
|  | 		if !match { | |||
|  | 			break | |||
|  | 		} | |||
|  | 
 | |||
|  | 		arg := VaUintptr(args) | |||
|  | 		switch mod { | |||
|  | 		case modNone: | |||
|  | 			*(*uint32)(unsafe.Pointer(arg)) = uint32(n) | |||
|  | 		case modH: | |||
|  | 			*(*uint16)(unsafe.Pointer(arg)) = uint16(n) | |||
|  | 		case modHH: | |||
|  | 			*(*byte)(unsafe.Pointer(arg)) = byte(n) | |||
|  | 		case modL: | |||
|  | 			*(*ulong)(unsafe.Pointer(arg)) = ulong(n) | |||
|  | 		default: | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 		nvalues = 1 | |||
|  | 	case 'f', 'e', 'g', 'E', 'a': | |||
|  | 		// Matches an optionally signed floating-point number; the next pointer must be | |||
|  | 		// a pointer to float. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 's': | |||
|  | 		// Matches  a  sequence of non-white-space characters; the next pointer must be | |||
|  | 		// a pointer to the initial element of a character array that is long enough to | |||
|  | 		// hold the input sequence and the terminating null byte ('\0'), which is added | |||
|  | 		// automatically.  The input string stops at white space or at the maximum | |||
|  | 		// field width, whichever occurs first. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'c': | |||
|  | 		// Matches a sequence of characters whose length is specified by the maximum | |||
|  | 		// field width (default 1); the next pointer must be a pointer to char, and | |||
|  | 		// there must be enough room for  all the characters (no terminating null byte | |||
|  | 		// is added).  The usual skip of leading white space is suppressed.  To skip | |||
|  | 		// white space first, use an explicit space in the format. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case '[': | |||
|  | 		// Matches  a nonempty sequence of characters from the specified set of | |||
|  | 		// accepted characters; the next pointer must be a pointer to char, and there | |||
|  | 		// must be enough room for all the char‐ acters in the string, plus a | |||
|  | 		// terminating null byte.  The usual skip of leading white space is suppressed. | |||
|  | 		// The string is to be made up of characters in (or not in) a particular set; | |||
|  | 		// the  set  is defined by the characters between the open bracket [ character | |||
|  | 		// and a close bracket ] character.  The set excludes those characters if the | |||
|  | 		// first character after the open bracket is a circumflex (^).  To include a | |||
|  | 		// close bracket in the set, make it the first character after the open bracket | |||
|  | 		// or the circumflex; any other position will end the set.   The hyphen | |||
|  | 		// character - is also special; when placed between two other characters, it | |||
|  | 		// adds all intervening characters to the set.  To include a hyphen, make it | |||
|  | 		// the last character before the final close bracket.  For instance, [^]0-9-] | |||
|  | 		// means the set "everything except close bracket, zero through nine, and | |||
|  | 		// hyphen".  The string ends with the appearance of a  character not in the | |||
|  | 		// (or, with a circumflex, in) set or when the field width runs out. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	case 'p': | |||
|  | 		// Matches a pointer value (as printed by %p in printf(3); the next pointer | |||
|  | 		// must be a pointer to a pointer to void. | |||
|  | 		format++ | |||
|  | 		skipReaderWhiteSpace(r) | |||
|  | 		c, err := r.ReadByte() | |||
|  | 		if err != nil { | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if c != '0' { | |||
|  | 			r.UnreadByte() | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if c, err = r.ReadByte(); err != nil { | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if c != 'x' && c != 'X' { | |||
|  | 			r.UnreadByte() | |||
|  | 			panic(todo("")) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		var digit, n uint64 | |||
|  | 	ptr: | |||
|  | 		for ; width != 0; width-- { | |||
|  | 			c, err := r.ReadByte() | |||
|  | 			if err != nil { | |||
|  | 				if match { | |||
|  | 					break ptr | |||
|  | 				} | |||
|  | 
 | |||
|  | 				panic(todo("")) | |||
|  | 			} | |||
|  | 
 | |||
|  | 			switch { | |||
|  | 			case c >= '0' && c <= '9': | |||
|  | 				digit = uint64(c) - '0' | |||
|  | 			case c >= 'a' && c <= 'f': | |||
|  | 				digit = uint64(c) - 'a' + 10 | |||
|  | 			case c >= 'A' && c <= 'F': | |||
|  | 				digit = uint64(c) - 'A' + 10 | |||
|  | 			default: | |||
|  | 				r.UnreadByte() | |||
|  | 				break ptr | |||
|  | 			} | |||
|  | 			match = true | |||
|  | 			n0 := n | |||
|  | 			n = n<<4 + digit | |||
|  | 			if n < n0 { | |||
|  | 				panic(todo("")) | |||
|  | 			} | |||
|  | 		} | |||
|  | 		if !match { | |||
|  | 			break | |||
|  | 		} | |||
|  | 
 | |||
|  | 		arg := VaUintptr(args) | |||
|  | 		*(*uintptr)(unsafe.Pointer(arg)) = uintptr(n) | |||
|  | 		nvalues = 1 | |||
|  | 	case 'n': | |||
|  | 		// Nothing is expected; instead, the number of characters consumed thus far | |||
|  | 		// from the input is stored through the next pointer, which must be a pointer | |||
|  | 		// to int.  This is not a conversion and does not increase the count returned | |||
|  | 		// by the function.  The assignment can be suppressed with the * | |||
|  | 		// assignment-suppression character, but the effect on the return value is | |||
|  | 		// undefined.  Therefore %*n conversions should not be used. | |||
|  | 		format++ | |||
|  | 		panic(todo("")) | |||
|  | 	default: | |||
|  | 		panic(todo("%#U", c)) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return format, nvalues, match | |||
|  | } | |||
|  | 
 | |||
|  | func skipReaderWhiteSpace(r *strings.Reader) error { | |||
|  | 	for { | |||
|  | 		c, err := r.ReadByte() | |||
|  | 		if err != nil { | |||
|  | 			return err | |||
|  | 		} | |||
|  | 
 | |||
|  | 		switch c { | |||
|  | 		case ' ', '\t', '\n', '\r', '\v', '\f': | |||
|  | 			// ok | |||
|  | 		default: | |||
|  | 			r.UnreadByte() | |||
|  | 			return nil | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func skipWhiteSpace(s uintptr) uintptr { | |||
|  | 	for { | |||
|  | 		switch c := *(*byte)(unsafe.Pointer(s)); c { | |||
|  | 		case ' ', '\t', '\n', '\r', '\v', '\f': | |||
|  | 			s++ | |||
|  | 		default: | |||
|  | 			return s | |||
|  | 		} | |||
|  | 	} | |||
|  | } |